- add multi select for storage api (kitkat)

- UI fixes
- refactoring
This commit is contained in:
mar-v-in 2014-07-31 22:21:46 +02:00
parent 1e4f0c6b00
commit 0c7eea225b
15 changed files with 136 additions and 126 deletions

View File

@ -149,8 +149,8 @@
<action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" />
<category android:name="android.intent.category.DEFAULT" />
<!-- TODO: accept other schemes! -->
<data android:scheme="file" />
<data android:scheme="content"/>
</intent-filter>
<!-- Android's Send Action -->
<intent-filter android:label="@string/intent_send_encrypt">
@ -200,8 +200,8 @@
<action android:name="org.sufficientlysecure.keychain.action.DECRYPT" />
<category android:name="android.intent.category.DEFAULT" />
<!-- TODO: accept other schemes! -->
<data android:scheme="file" />
<data android:scheme="content"/>
</intent-filter>
<!-- Android's Send Action -->
<intent-filter android:label="@string/intent_send_decrypt">
@ -221,6 +221,7 @@
<data android:host="*" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*"/>
<!-- Workaround to match files in pathes with dots in them, like /cdcard/my.folder/test.gpg -->
<data android:pathPattern=".*\\.gpg" />
<data android:pathPattern=".*\\..*\\.gpg" />

View File

@ -26,18 +26,12 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.*;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
@ -135,18 +129,25 @@ public class FileHelper {
}, fragment.getActivity().getSupportFragmentManager(), title, message, defaultFile, checkMsg);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public static void openDocument(Fragment fragment, String mimeType, int requestCode) {
openDocument(fragment, mimeType, false, requestCode);
}
/**
* Opens the storage browser on Android 4.4 or later for opening a file
* @param fragment
* @param mimeType can be text/plain for example
* @param multiple allow file chooser to return multiple files
* @param requestCode used to identify the result coming back from storage browser onActivityResult() in your
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
public static void openDocument(Fragment fragment, String mimeType, int requestCode) {
public static void openDocument(Fragment fragment, String mimeType, boolean multiple, int requestCode) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(mimeType);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple);
fragment.startActivityForResult(intent, requestCode);
}

View File

@ -104,6 +104,8 @@ public class TemporaryStorageProvider extends ContentProvider {
@Override
public String getType(Uri uri) {
// Note: If we can find a files mime type, we can decrypt it to temp storage and open it after
// encryption. The mime type is needed, else UI really sucks and some apps break.
return "*/*";
}

View File

@ -23,7 +23,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
@ -120,6 +119,7 @@ public class DecryptActivity extends DrawerActivity {
// override action
action = ACTION_DECRYPT;
mFileFragmentBundle.putBoolean(DecryptFileFragment.ARG_FROM_VIEW_INTENT, true);
}
String textData = extras.getString(EXTRA_TEXT);

View File

@ -29,21 +29,21 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.util.Notify;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
import java.io.File;
public class DecryptFileFragment extends DecryptFragment {
public static final String ARG_URI = "uri";
public static final String ARG_FROM_VIEW_INTENT = "view_intent";
private static final int REQUEST_CODE_INPUT = 0x00007003;
private static final int REQUEST_CODE_OUTPUT = 0x00007007;
@ -189,6 +189,15 @@ public class DecryptFileFragment extends DecryptFragment {
deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
setInputUri(null);
}
/*
// A future open after decryption feature
if () {
Intent viewFile = new Intent(Intent.ACTION_VIEW);
viewFile.setData(mOutputUri);
startActivity(viewFile);
}
*/
}
}
}

View File

@ -27,9 +27,7 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;

View File

@ -28,7 +28,6 @@ import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.view.Menu;
import android.view.MenuItem;
import com.devspark.appmsg.AppMsg;
@ -41,8 +40,8 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Choice;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@ -220,9 +219,12 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
if (!isContentMessage() && mDeleteAfterEncrypt) {
// TODO: Create and show dialog to delete original file
//DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);
//deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
//setInputUri(null);
for (Uri inputUri : mInputUris) {
DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUri);
deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
}
mInputUris.clear();
notifyUpdate();
}
if (mShareAfterEncrypt) {
@ -327,12 +329,11 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
AppMsg.makeText(this, R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
return false;
} else if (mInputUris.size() > 1 && !mShareAfterEncrypt) {
AppMsg.makeText(this, "TODO", AppMsg.STYLE_ALERT).show(); // TODO
// This should be impossible...
return false;
} else if (mInputUris.size() != mOutputUris.size()) {
// This as well
return false;
}
if (mInputUris.size() != mOutputUris.size()) {
throw new IllegalStateException("Something went terribly wrong if this happens!");
}
}
@ -445,6 +446,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
switch (item.getItemId()) {
case R.id.check_use_symmetric:
mSwitchToMode = item.isChecked() ? PAGER_MODE_SYMMETRIC : PAGER_MODE_ASYMMETRIC;
mViewPagerMode.setCurrentItem(mSwitchToMode);
notifyUpdate();
break;

View File

@ -37,7 +37,6 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView;
@ -48,14 +47,9 @@ import java.util.Iterator;
import java.util.List;
public class EncryptAsymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener {
private static final String SIGN_KEY_SELECTION = KeyRings.CAN_SIGN + " = 1 AND " + KeyRings.IS_REVOKED + " = 0";
public static final String ARG_SIGNATURE_KEY_ID = "signature_key_id";
public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids";
public static final int REQUEST_CODE_PUBLIC_KEYS = 0x00007001;
public static final int REQUEST_CODE_SECRET_KEYS = 0x00007002;
ProviderHelper mProviderHelper;
// view
@ -130,26 +124,6 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
// preselect keys given by arguments (given by Intent to EncryptActivity)
preselectKeys(signatureKeyId, encryptionKeyIds, mProviderHelper);
// TODO: Move this into widget!
getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), KeychainContract.KeyRings.buildUnifiedKeyRingsUri(),
new String[]{KeyRings.HAS_ENCRYPT, KeyRings.KEY_ID, KeyRings.USER_ID, KeyRings.FINGERPRINT},
null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mEncryptKeyView.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mEncryptKeyView.swapCursor(null);
}
});
getLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

View File

@ -17,24 +17,27 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
import org.sufficientlysecure.keychain.util.Choice;
import java.io.File;
import java.util.List;
@ -89,45 +92,13 @@ public class EncryptFileFragment extends Fragment implements EncryptActivityInte
mAddView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (Constants.KITKAT) {
FileHelper.openDocument(EncryptFileFragment.this, "*/*", REQUEST_CODE_INPUT);
} else {
FileHelper.openFile(EncryptFileFragment.this,
mEncryptInterface.getInputUris().isEmpty() ? null : mEncryptInterface.getInputUris().get(mEncryptInterface.getInputUris().size() - 1), "*/*", REQUEST_CODE_INPUT);
}
addInputUri();
}
});
ListView listView = (ListView) view.findViewById(R.id.selected_files_list);
listView.addFooterView(mAddView);
listView.setAdapter(mAdapter);
/*
mFileCompression = (Spinner) view.findViewById(R.id.fileCompression);
Choice[] choices = new Choice[]{
new Choice(Constants.choice.compression.none, getString(R.string.choice_none) + " ("
+ getString(R.string.compression_fast) + ")"),
new Choice(Constants.choice.compression.zip, "ZIP ("
+ getString(R.string.compression_fast) + ")"),
new Choice(Constants.choice.compression.zlib, "ZLIB ("
+ getString(R.string.compression_fast) + ")"),
new Choice(Constants.choice.compression.bzip2, "BZIP2 ("
+ getString(R.string.compression_very_slow) + ")"),
};
ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getActivity(),
android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mFileCompression.setAdapter(adapter);
int defaultFileCompression = Preferences.getPreferences(getActivity()).getDefaultFileCompression();
for (int i = 0; i < choices.length; ++i) {
if (choices[i].getId() == defaultFileCompression) {
mFileCompression.setSelection(i);
break;
}
}
*/
return view;
}
@ -138,6 +109,16 @@ public class EncryptFileFragment extends Fragment implements EncryptActivityInte
addInputUris(getArguments().<Uri>getParcelableArrayList(ARG_URIS));
}
private void addInputUri() {
if (Constants.KITKAT) {
FileHelper.openDocument(EncryptFileFragment.this, "*/*", true, REQUEST_CODE_INPUT);
} else {
FileHelper.openFile(EncryptFileFragment.this, mEncryptInterface.getInputUris().isEmpty() ?
null : mEncryptInterface.getInputUris().get(mEncryptInterface.getInputUris().size() - 1),
"*/*", REQUEST_CODE_INPUT);
}
}
private void addInputUris(List<Uri> uris) {
if (uris != null) {
for (Uri uri : uris) {
@ -206,25 +187,41 @@ public class EncryptFileFragment extends Fragment implements EncryptActivityInte
(mEncryptInterface.isUseArmor() ? ".asc" : ".gpg");
mEncryptInterface.getOutputUris().add(TemporaryStorageProvider.createFile(getActivity(), targetName));
}
mEncryptInterface.startEncrypt(share);
mEncryptInterface.startEncrypt(true);
} else if (mEncryptInterface.getInputUris().size() == 1) {
showOutputFileDialog();
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public boolean handleClipData(Intent data) {
if (data.getClipData() != null && data.getClipData().getItemCount() > 0) {
for (int i = 0; i < data.getClipData().getItemCount(); i++) {
Uri uri = data.getClipData().getItemAt(i).getUri();
if (uri != null) addInputUri(uri);
}
return true;
}
return false;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_INPUT: {
if (resultCode == Activity.RESULT_OK && data != null) {
if (!Constants.KITKAT || !handleClipData(data)) {
addInputUri(data.getData());
}
}
return;
}
case REQUEST_CODE_OUTPUT: {
// This happens after output file was selected, so start our operation
if (resultCode == Activity.RESULT_OK && data != null) {
mEncryptInterface.getOutputUris().clear();
mEncryptInterface.getOutputUris().add(data.getData());
mEncryptInterface.notifyUpdate();
mEncryptInterface.startEncrypt(false);
}
return;

View File

@ -4,6 +4,11 @@ import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@ -48,8 +53,6 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
allowDuplicates(false);
}
private EncryptKeyAdapter mAdapter;
@Override
protected View getViewForObject(Object object) {
if (object instanceof EncryptionKey) {
@ -81,6 +84,31 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
return null;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (getContext() instanceof FragmentActivity) {
((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getContext(), KeychainContract.KeyRings.buildUnifiedKeyRingsUri(),
new String[]{KeychainContract.KeyRings.HAS_ENCRYPT, KeychainContract.KeyRings.KEY_ID, KeychainContract.KeyRings.USER_ID, KeychainContract.KeyRings.FINGERPRINT},
null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
swapCursor(null);
}
});
}
}
public void swapCursor(Cursor cursor) {
if (cursor == null) {
setAdapter(new EncryptKeyAdapter(Collections.<EncryptionKey>emptyList()));

View File

@ -17,12 +17,13 @@ public class NoSwipeWrapContentViewPager extends android.support.v4.view.ViewPag
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
for(int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
int height;
View child = getChildAt(getCurrentItem());
if (child != null) {
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if(h > height) height = h;
height = child.getMeasuredHeight();
} else {
height = 0;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@ -22,16 +23,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="From: "/>
android:text="@string/from"/>
<Spinner
android:id="@+id/sign"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/label_sign"
android:padding="0dp"
android:layout_margin="0dp"
/>
android:layout_gravity="center_vertical"/>
</LinearLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content_frame"
android:layout_marginLeft="@dimen/drawer_content_padding"
android:layout_width="match_parent"
@ -9,7 +10,8 @@
<org.sufficientlysecure.keychain.ui.widget.NoSwipeWrapContentViewPager
android:id="@+id/encrypt_pager_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="match_parent"
android:orientation="vertical">
</org.sufficientlysecure.keychain.ui.widget.NoSwipeWrapContentViewPager>

View File

@ -10,17 +10,11 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider"
android:layout_marginBottom="8dp"/>
<ListView
android:id="@+id/selected_files_list"
android:dividerHeight="4dip"
android:divider="@android:color/transparent"
android:layout_marginTop="8dp"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"/>
@ -30,6 +24,8 @@
android:layout_height="1dip"
android:background="?android:attr/listDivider"/>
<!-- Note: The following construct should be a widget, we use it quiet often -->
<LinearLayout
android:id="@+id/action_encrypt_share"
android:paddingLeft="8dp"

View File

@ -56,6 +56,7 @@
<string name="btn_decrypt_verify_clipboard">From Clipboard</string>
<string name="btn_encrypt_file">Encrypt and save file</string>
<string name="btn_encrypt_share_file">Encrypt and share file</string>
<string name="btn_encrypt_sign_share">Encrypt, sign and share</string>
<string name="btn_save">Save</string>
<string name="btn_do_not_save">Cancel</string>
<string name="btn_delete">Delete</string>
@ -166,6 +167,7 @@
</plurals>
<string name="secret_key">Secret Key:</string>
<string name="from">From:</string>
<!-- choice -->
<string name="choice_none">None</string>