From 76db0b3b8e8c474a50e17b165657c1a24f8e6be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 26 Mar 2015 11:04:20 +0100 Subject: Start refactoring encrypt ui, EncryptFileActivity no longer crashing --- .../keychain/ui/EncryptFilesFragment.java | 436 +++++++++++++++++++-- 1 file changed, 396 insertions(+), 40 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 4ba76d8ea..771800245 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -19,14 +19,18 @@ package org.sufficientlysecure.keychain.ui; import android.annotation.TargetApi; import android.app.Activity; +import android.app.ProgressDialog; 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.os.Message; +import android.os.Messenger; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -35,39 +39,108 @@ import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; +import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.PgpConstants; +import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.ServiceProgressHandler; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.FileHelper; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; +import org.sufficientlysecure.keychain.util.ShareHelper; import java.io.File; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Set; -public class EncryptFilesFragment extends Fragment implements EncryptActivityInterface.UpdateListener { - public static final String ARG_URIS = "uris"; +public class EncryptFilesFragment extends CryptoOperationFragment { private static final int REQUEST_CODE_INPUT = 0x00007003; private static final int REQUEST_CODE_OUTPUT = 0x00007007; - private EncryptActivityInterface mEncryptInterface; + public interface IMode { + + public void onModeChanged(boolean symmetric); + } + + private IMode mModeInterface; + + // model used by fragments + private boolean mSymmetricMode = true; + private boolean mUseArmor = false; + private boolean mUseCompression = true; + private boolean mDeleteAfterEncrypt = false; + private boolean mShareAfterEncrypt = false; + private boolean mEncryptFilenames = true; + private boolean mHiddenRecipients = false; + + private long mEncryptionKeyIds[] = null; + private String mEncryptionUserIds[] = null; + private long mSigningKeyId = Constants.key.none; + private Passphrase mPassphrase = new Passphrase(); + + private ArrayList mInputUris = new ArrayList(); + private ArrayList mOutputUris = new ArrayList(); - // view - private View mAddView; private ListView mSelectedFiles; private SelectedFilesAdapter mAdapter = new SelectedFilesAdapter(); private final Map thumbnailCache = new HashMap<>(); + + public static final String ARG_USE_ASCII_ARMOR = "use_ascii_armor"; + public static final String ARG_URIS = "uris"; + + + /** + * Creates new instance of this fragment + */ + public static EncryptFilesFragment newInstance(ArrayList uris, boolean useArmor) { + EncryptFilesFragment frag = new EncryptFilesFragment(); + + Bundle args = new Bundle(); + args.putBoolean(ARG_USE_ASCII_ARMOR, useArmor); + args.putParcelableArrayList(ARG_URIS, uris); + frag.setArguments(args); + + return frag; + } + + public void setEncryptionKeyIds(long[] encryptionKeyIds) { + mEncryptionKeyIds = encryptionKeyIds; + } + + public void setEncryptionUserIds(String[] encryptionUserIds) { + mEncryptionUserIds = encryptionUserIds; + } + + public void setSigningKeyId(long signingKeyId) { + mSigningKeyId = signingKeyId; + } + + public void setPassphrase(Passphrase passphrase) { + mPassphrase = passphrase; + } + @Override public void onAttach(Activity activity) { super.onAttach(activity); try { - mEncryptInterface = (EncryptActivityInterface) activity; + mModeInterface = (IMode) activity; } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface"); + throw new ClassCastException(activity.toString() + " must be IMode"); } } @@ -78,15 +151,15 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_files_fragment, container, false); - mAddView = inflater.inflate(R.layout.file_list_entry_add, null); - mAddView.setOnClickListener(new View.OnClickListener() { + View addView = inflater.inflate(R.layout.file_list_entry_add, null); + addView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addInputUri(); } }); mSelectedFiles = (ListView) view.findViewById(R.id.selected_files_list); - mSelectedFiles.addFooterView(mAddView); + mSelectedFiles.addFooterView(addView); mSelectedFiles.setAdapter(mAdapter); return view; @@ -95,16 +168,18 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); + + mInputUris = getArguments().getParcelableArrayList(ARG_URIS); + mUseArmor = getArguments().getBoolean(ARG_USE_ASCII_ARMOR); } private void addInputUri() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { FileHelper.openDocument(EncryptFilesFragment.this, "*/*", true, REQUEST_CODE_INPUT); } else { - FileHelper.openFile(EncryptFilesFragment.this, mEncryptInterface.getInputUris().isEmpty() ? - null : mEncryptInterface.getInputUris().get(mEncryptInterface.getInputUris().size() - 1), + FileHelper.openFile(EncryptFilesFragment.this, mInputUris.isEmpty() ? + null : mInputUris.get(mInputUris.size() - 1), "*/*", REQUEST_CODE_INPUT); } } @@ -114,32 +189,30 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt return; } - if (mEncryptInterface.getInputUris().contains(inputUri)) { + if (mInputUris.contains(inputUri)) { Notify.create(getActivity(), getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)), Notify.Style.ERROR).show(this); return; } - mEncryptInterface.getInputUris().add(inputUri); - mEncryptInterface.notifyUpdate(); + mInputUris.add(inputUri); mSelectedFiles.requestFocus(); } private void delInputUri(int position) { - mEncryptInterface.getInputUris().remove(position); - mEncryptInterface.notifyUpdate(); + mInputUris.remove(position); mSelectedFiles.requestFocus(); } private void showOutputFileDialog() { - if (mEncryptInterface.getInputUris().size() > 1 || mEncryptInterface.getInputUris().isEmpty()) { + if (mInputUris.size() > 1 || mInputUris.isEmpty()) { throw new IllegalStateException(); } - Uri inputUri = mEncryptInterface.getInputUris().get(0); + Uri inputUri = mInputUris.get(0); String targetName = - (mEncryptInterface.isEncryptFilenames() ? "1" : FileHelper.getFilename(getActivity(), inputUri)) - + (mEncryptInterface.isUseArmor() ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); + (mEncryptFilenames ? "1" : FileHelper.getFilename(getActivity(), inputUri)) + + (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { File file = new File(inputUri.getPath()); File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; @@ -152,23 +225,23 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt } private void encryptClicked(boolean share) { - if (mEncryptInterface.getInputUris().isEmpty()) { + if (mInputUris.isEmpty()) { Notify.create(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR).show(this); return; } if (share) { - mEncryptInterface.getOutputUris().clear(); + mOutputUris.clear(); int filenameCounter = 1; - for (Uri uri : mEncryptInterface.getInputUris()) { + for (Uri uri : mInputUris) { String targetName = - (mEncryptInterface.isEncryptFilenames() ? String.valueOf(filenameCounter) : FileHelper.getFilename(getActivity(), uri)) - + (mEncryptInterface.isUseArmor() ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); - mEncryptInterface.getOutputUris().add(TemporaryStorageProvider.createFile(getActivity(), targetName)); + (mEncryptFilenames ? String.valueOf(filenameCounter) : FileHelper.getFilename(getActivity(), uri)) + + (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); + mOutputUris.add(TemporaryStorageProvider.createFile(getActivity(), targetName)); filenameCounter++; } - mEncryptInterface.startEncrypt(true); + startEncrypt(true); } else { - if (mEncryptInterface.getInputUris().size() > 1) { + if (mInputUris.size() > 1) { Notify.create(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR).show(this); return; } @@ -188,8 +261,17 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt return false; } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.encrypt_file_activity, menu); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { + if (item.isCheckable()) { + item.setChecked(!item.isChecked()); + } switch (item.getItemId()) { case R.id.encrypt_save: { encryptClicked(false); @@ -199,6 +281,36 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt encryptClicked(true); break; } + case R.id.check_use_symmetric: { + mSymmetricMode = item.isChecked(); + mModeInterface.onModeChanged(mSymmetricMode); + break; + } + case R.id.check_use_armor: { + mUseArmor = item.isChecked(); +// notifyUpdate(); + break; + } + case R.id.check_delete_after_encrypt: { + mDeleteAfterEncrypt = item.isChecked(); +// notifyUpdate(); + break; + } + case R.id.check_enable_compression: { + mUseCompression = item.isChecked(); +// onNotifyUpdate(); + break; + } + case R.id.check_encrypt_filenames: { + mEncryptFilenames = item.isChecked(); +// onNotifyUpdate(); + break; + } +// case R.id.check_hidden_recipients: { +// mHiddenRecipients = item.isChecked(); +// notifyUpdate(); +// break; +// } default: { return super.onOptionsItemSelected(item); } @@ -206,6 +318,251 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt return true; } + protected boolean inputIsValid() { + // file checks + + if (mInputUris.isEmpty()) { + Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR) + .show(this); + return false; + } else if (mInputUris.size() > 1 && !mShareAfterEncrypt) { + // This should be impossible... + return false; + } else if (mInputUris.size() != mOutputUris.size()) { + // This as well + return false; + } + + if (mSymmetricMode) { + // symmetric encryption checks + + if (mPassphrase == null) { + Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) + .show(this); + return false; + } + if (mPassphrase.isEmpty()) { + Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) + .show(this); + return false; + } + + } else { + // asymmetric encryption checks + + boolean gotEncryptionKeys = (mEncryptionKeyIds != null + && mEncryptionKeyIds.length > 0); + + // Files must be encrypted, only text can be signed-only right now + if (!gotEncryptionKeys) { + Notify.create(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR) + .show(this); + return false; + } + } + return true; + } + + public void startEncrypt(boolean share) { + mShareAfterEncrypt = share; + startEncrypt(); + } + + public void onEncryptSuccess(final SignEncryptResult result) { + if (mDeleteAfterEncrypt) { + final Uri[] inputUris = mInputUris.toArray(new Uri[mInputUris.size()]); + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUris); + deleteFileDialog.setOnDeletedListener(new DeleteFileDialogFragment.OnDeletedListener() { + + @Override + public void onDeleted() { + if (mShareAfterEncrypt) { + // Share encrypted message/file + startActivity(sendWithChooserExcludingEncrypt()); + } else { + // Save encrypted file + result.createNotify(getActivity()).show(); + } + } + + }); + deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog"); + + mInputUris.clear(); + onNotifyUpdate(); + } else { + if (mShareAfterEncrypt) { + // Share encrypted message/file + startActivity(sendWithChooserExcludingEncrypt()); + } else { + // Save encrypted file + result.createNotify(getActivity()).show(); + } + } + } + + protected SignEncryptParcel createEncryptBundle() { + // fill values for this action + SignEncryptParcel data = new SignEncryptParcel(); + + data.addInputUris(mInputUris); + data.addOutputUris(mOutputUris); + + if (mUseCompression) { + data.setCompressionId(PgpConstants.sPreferredCompressionAlgorithms.get(0)); + } else { + data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); + } + data.setHiddenRecipients(mHiddenRecipients); + data.setEnableAsciiArmorOutput(mUseArmor); + data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); + data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); + + if (mSymmetricMode) { + Log.d(Constants.TAG, "Symmetric encryption enabled!"); + Passphrase passphrase = mPassphrase; + if (passphrase.isEmpty()) { + passphrase = null; + } + data.setSymmetricPassphrase(passphrase); + } else { + data.setEncryptionMasterKeyIds(mEncryptionKeyIds); + data.setSignatureMasterKeyId(mSigningKeyId); +// data.setSignaturePassphrase(mSigningKeyPassphrase); + } + return data; + } + + /** + * Create Intent Chooser but exclude OK's EncryptActivity. + */ + private Intent sendWithChooserExcludingEncrypt() { + Intent prototype = createSendIntent(); + String title = getString(R.string.title_share_file); + + // we don't want to encrypt the encrypted, no inception ;) + String[] blacklist = new String[]{ + Constants.PACKAGE_NAME + ".ui.EncryptFileActivity", + "org.thialfihar.android.apg.ui.EncryptActivity" + }; + + return new ShareHelper(getActivity()).createChooserExcluding(prototype, title, blacklist); + } + + private Intent createSendIntent() { + Intent sendIntent; + // file + if (mOutputUris.size() == 1) { + sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris.get(0)); + } else { + sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE); + sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris); + } + sendIntent.setType(Constants.ENCRYPTED_FILES_MIME); + + if (!mSymmetricMode && mEncryptionUserIds != null) { + Set users = new HashSet<>(); + for (String user : mEncryptionUserIds) { + KeyRing.UserId userId = KeyRing.splitUserId(user); + if (userId.email != null) { + users.add(userId.email); + } + } + sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); + } + return sendIntent; + } + + public void startEncrypt() { + cryptoOperation(new CryptoInputParcel()); + } + + // public void startEncrypt(CryptoInputParcel cryptoInput) { + @Override + protected void cryptoOperation(CryptoInputParcel cryptoInput) { + + if (!inputIsValid()) { + // Notify was created by inputIsValid. + return; + } + + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); + + final SignEncryptParcel input = createEncryptBundle(); + if (cryptoInput != null) { + input.setCryptoInput(cryptoInput); + } + + Bundle data = new Bundle(); + data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after encrypting is done in KeychainIntentService + ServiceProgressHandler serviceHandler = new ServiceProgressHandler( + getActivity(), + getString(R.string.progress_encrypting), + ProgressDialog.STYLE_HORIZONTAL, + true, + ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + // handle pending messages + if (handlePendingMessage(message)) { + return; + } + + if (message.arg1 == MessageStatus.OKAY.ordinal()) { + SignEncryptResult result = + message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT); + +// PgpSignEncryptResult pgpResult = result.getPending(); +// +// if (pgpResult != null && pgpResult.isPending()) { +// if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) == +// PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) { +// startPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); +// } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) == +// PgpSignEncryptResult.RESULT_PENDING_NFC) { +// +// RequiredInputParcel parcel = RequiredInputParcel.createNfcSignOperation( +// pgpResult.getNfcHash(), +// pgpResult.getNfcAlgo(), +// input.getSignatureTime()); +// startNfcSign(pgpResult.getNfcKeyId(), parcel); +// +// } else { +// throw new RuntimeException("Unhandled pending result!"); +// } +// return; +// } + + if (result.success()) { + onEncryptSuccess(result); + } else { + result.createNotify(getActivity()).show(); + } + + // no matter the result, reset parameters +// mSigningKeyPassphrase = null; + } + } + }; + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(serviceHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + serviceHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { @@ -220,10 +577,10 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt 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); + mOutputUris.clear(); + mOutputUris.add(data.getData()); + onNotifyUpdate(); + startEncrypt(false); } return; } @@ -236,11 +593,10 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt } } - @Override public void onNotifyUpdate() { // Clear cache if needed for (Uri uri : new HashSet<>(thumbnailCache.keySet())) { - if (!mEncryptInterface.getInputUris().contains(uri)) { + if (!mInputUris.contains(uri)) { thumbnailCache.remove(uri); } } @@ -251,12 +607,12 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt private class SelectedFilesAdapter extends BaseAdapter { @Override public int getCount() { - return mEncryptInterface.getInputUris().size(); + return mInputUris.size(); } @Override public Object getItem(int position) { - return mEncryptInterface.getInputUris().get(position); + return mInputUris.get(position); } @Override @@ -266,7 +622,7 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt @Override public View getView(final int position, View convertView, ViewGroup parent) { - Uri inputUri = mEncryptInterface.getInputUris().get(position); + Uri inputUri = mInputUris.get(position); View view; if (convertView == null) { view = getActivity().getLayoutInflater().inflate(R.layout.file_list_entry, null); -- cgit v1.2.3 From 13f4cc4ad32eb412e2ecdb649dcdd0788d30d5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 27 Mar 2015 00:40:37 +0100 Subject: Refactoring of EncryptTextActivity --- .../keychain/ui/EncryptFilesFragment.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 771800245..fb9a86b07 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -68,17 +68,18 @@ import java.util.Set; public class EncryptFilesFragment extends CryptoOperationFragment { - private static final int REQUEST_CODE_INPUT = 0x00007003; - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - public interface IMode { - public void onModeChanged(boolean symmetric); } + public static final String ARG_USE_ASCII_ARMOR = "use_ascii_armor"; + public static final String ARG_URIS = "uris"; + + private static final int REQUEST_CODE_INPUT = 0x00007003; + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + private IMode mModeInterface; - // model used by fragments private boolean mSymmetricMode = true; private boolean mUseArmor = false; private boolean mUseCompression = true; @@ -99,11 +100,6 @@ public class EncryptFilesFragment extends CryptoOperationFragment { private SelectedFilesAdapter mAdapter = new SelectedFilesAdapter(); private final Map thumbnailCache = new HashMap<>(); - - public static final String ARG_USE_ASCII_ARMOR = "use_ascii_armor"; - public static final String ARG_URIS = "uris"; - - /** * Creates new instance of this fragment */ -- cgit v1.2.3 From 95f1527afe81c59a116cadc2ed37c065da1819ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 29 Mar 2015 20:37:54 +0200 Subject: Fixing crashes with new encrypt ui --- .../keychain/ui/EncryptFilesFragment.java | 29 +++++++++++----------- 1 file changed, 14 insertions(+), 15 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index fb9a86b07..5af353524 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -80,7 +80,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { private IMode mModeInterface; - private boolean mSymmetricMode = true; + private boolean mSymmetricMode = false; private boolean mUseArmor = false; private boolean mUseCompression = true; private boolean mDeleteAfterEncrypt = false; @@ -188,7 +188,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { if (mInputUris.contains(inputUri)) { Notify.create(getActivity(), getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)), - Notify.Style.ERROR).show(this); + Notify.Style.ERROR).show(); return; } @@ -222,7 +222,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { private void encryptClicked(boolean share) { if (mInputUris.isEmpty()) { - Notify.create(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR).show(this); + Notify.create(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR).show(); return; } if (share) { @@ -238,7 +238,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { startEncrypt(true); } else { if (mInputUris.size() > 1) { - Notify.create(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR).show(this); + Notify.create(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR).show(); return; } showOutputFileDialog(); @@ -260,7 +260,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.encrypt_file_activity, menu); + inflater.inflate(R.menu.encrypt_file_fragment, menu); } @Override @@ -319,12 +319,14 @@ public class EncryptFilesFragment extends CryptoOperationFragment { if (mInputUris.isEmpty()) { Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR) - .show(this); + .show(); return false; } else if (mInputUris.size() > 1 && !mShareAfterEncrypt) { + Log.e(Constants.TAG, "Aborting: mInputUris.size() > 1 && !mShareAfterEncrypt"); // This should be impossible... return false; } else if (mInputUris.size() != mOutputUris.size()) { + Log.e(Constants.TAG, "Aborting: mInputUris.size() != mOutputUris.size()"); // This as well return false; } @@ -334,12 +336,12 @@ public class EncryptFilesFragment extends CryptoOperationFragment { if (mPassphrase == null) { Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) - .show(this); + .show(); return false; } if (mPassphrase.isEmpty()) { Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) - .show(this); + .show(); return false; } @@ -352,7 +354,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { // Files must be encrypted, only text can be signed-only right now if (!gotEncryptionKeys) { Notify.create(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR) - .show(this); + .show(); return false; } } @@ -361,7 +363,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { public void startEncrypt(boolean share) { mShareAfterEncrypt = share; - startEncrypt(); + cryptoOperation(new CryptoInputParcel()); } public void onEncryptSuccess(final SignEncryptResult result) { @@ -470,18 +472,15 @@ public class EncryptFilesFragment extends CryptoOperationFragment { return sendIntent; } - public void startEncrypt() { - cryptoOperation(new CryptoInputParcel()); - } - - // public void startEncrypt(CryptoInputParcel cryptoInput) { @Override protected void cryptoOperation(CryptoInputParcel cryptoInput) { if (!inputIsValid()) { // Notify was created by inputIsValid. + Log.d(Constants.TAG, "Input not valid!"); return; } + Log.d(Constants.TAG, "Input valid!"); // Send all information needed to service to edit key in other thread Intent intent = new Intent(getActivity(), KeychainIntentService.class); -- cgit v1.2.3 From d7b79e55fba170a0fe691f00dbc943d8ecfff33e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 30 Mar 2015 16:40:41 +0200 Subject: pass CryptoInputParcel independently for SignEncryptOperation --- .../sufficientlysecure/keychain/ui/EncryptFilesFragment.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 5af353524..7e4c48e10 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -93,8 +93,8 @@ public class EncryptFilesFragment extends CryptoOperationFragment { private long mSigningKeyId = Constants.key.none; private Passphrase mPassphrase = new Passphrase(); - private ArrayList mInputUris = new ArrayList(); - private ArrayList mOutputUris = new ArrayList(); + private ArrayList mInputUris = new ArrayList<>(); + private ArrayList mOutputUris = new ArrayList<>(); private ListView mSelectedFiles; private SelectedFilesAdapter mAdapter = new SelectedFilesAdapter(); @@ -136,7 +136,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { try { mModeInterface = (IMode) activity; } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must be IMode"); + throw new ClassCastException(activity + " must be IMode"); } } @@ -487,12 +487,10 @@ public class EncryptFilesFragment extends CryptoOperationFragment { intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); final SignEncryptParcel input = createEncryptBundle(); - if (cryptoInput != null) { - input.setCryptoInput(cryptoInput); - } Bundle data = new Bundle(); data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input); + data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // Message is received after encrypting is done in KeychainIntentService -- cgit v1.2.3 From a3276a448528cee3ed392bf01e36835bbe8246ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 30 Mar 2015 20:41:29 +0200 Subject: Use RecyclerView in EncryptFilesFragment --- .../keychain/ui/EncryptFilesFragment.java | 311 ++++++++++++++------- 1 file changed, 208 insertions(+), 103 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 5af353524..dad8704fb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014-2015 Dominik Schürmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,6 @@ package org.sufficientlysecure.keychain.ui; -import android.annotation.TargetApi; import android.app.Activity; import android.app.ProgressDialog; import android.content.Intent; @@ -28,15 +27,17 @@ import android.os.Build; import android.os.Bundle; import android.os.Message; import android.os.Messenger; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.BaseAdapter; +import android.widget.Button; import android.widget.ImageView; -import android.widget.ListView; import android.widget.TextView; import org.spongycastle.bcpg.CompressionAlgorithmTags; @@ -50,6 +51,7 @@ import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; @@ -61,9 +63,8 @@ import org.sufficientlysecure.keychain.util.ShareHelper; import java.io.File; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; +import java.util.List; import java.util.Set; public class EncryptFilesFragment extends CryptoOperationFragment { @@ -93,12 +94,12 @@ public class EncryptFilesFragment extends CryptoOperationFragment { private long mSigningKeyId = Constants.key.none; private Passphrase mPassphrase = new Passphrase(); - private ArrayList mInputUris = new ArrayList(); - private ArrayList mOutputUris = new ArrayList(); + private ArrayList mOutputUris = new ArrayList<>(); - private ListView mSelectedFiles; - private SelectedFilesAdapter mAdapter = new SelectedFilesAdapter(); - private final Map thumbnailCache = new HashMap<>(); + private RecyclerView mSelectedFiles; + + ArrayList mFilesModels; + FilesAdapter mFilesAdapter; /** * Creates new instance of this fragment @@ -146,17 +147,28 @@ public class EncryptFilesFragment extends CryptoOperationFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_files_fragment, container, false); + mSelectedFiles = (RecyclerView) view.findViewById(R.id.selected_files_list); + + mSelectedFiles.addItemDecoration(new SpacesItemDecoration( + FormattingUtils.dpToPx(getActivity(), 4))); + mSelectedFiles.setHasFixedSize(true); + mSelectedFiles.setLayoutManager(new LinearLayoutManager(getActivity())); + mSelectedFiles.setItemAnimator(new DefaultItemAnimator()); - View addView = inflater.inflate(R.layout.file_list_entry_add, null); - addView.setOnClickListener(new View.OnClickListener() { + mFilesModels = new ArrayList<>(); + mFilesAdapter = new FilesAdapter(getActivity(), mFilesModels, new View.OnClickListener() { @Override public void onClick(View v) { addInputUri(); } }); - mSelectedFiles = (ListView) view.findViewById(R.id.selected_files_list); - mSelectedFiles.addFooterView(addView); - mSelectedFiles.setAdapter(mAdapter); + + ArrayList inputUris = getArguments().getParcelableArrayList(ARG_URIS); + if (inputUris != null) { + mFilesAdapter.addAll(inputUris); + } + mUseArmor = getArguments().getBoolean(ARG_USE_ASCII_ARMOR); + mSelectedFiles.setAdapter(mFilesAdapter); return view; } @@ -165,17 +177,14 @@ public class EncryptFilesFragment extends CryptoOperationFragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); - - mInputUris = getArguments().getParcelableArrayList(ARG_URIS); - mUseArmor = getArguments().getBoolean(ARG_USE_ASCII_ARMOR); } private void addInputUri() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { FileHelper.openDocument(EncryptFilesFragment.this, "*/*", true, REQUEST_CODE_INPUT); } else { - FileHelper.openFile(EncryptFilesFragment.this, mInputUris.isEmpty() ? - null : mInputUris.get(mInputUris.size() - 1), + FileHelper.openFile(EncryptFilesFragment.this, mFilesModels.isEmpty() ? + null : mFilesModels.get(mFilesModels.size() - 1).inputUri, "*/*", REQUEST_CODE_INPUT); } } @@ -185,32 +194,27 @@ public class EncryptFilesFragment extends CryptoOperationFragment { return; } - if (mInputUris.contains(inputUri)) { + if (mFilesAdapter.getAsArrayList().contains(inputUri)) { Notify.create(getActivity(), getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)), Notify.Style.ERROR).show(); return; } - mInputUris.add(inputUri); - mSelectedFiles.requestFocus(); - } - - private void delInputUri(int position) { - mInputUris.remove(position); + mFilesAdapter.add(inputUri); mSelectedFiles.requestFocus(); } private void showOutputFileDialog() { - if (mInputUris.size() > 1 || mInputUris.isEmpty()) { + if (mFilesModels.size() > 1 || mFilesModels.isEmpty()) { throw new IllegalStateException(); } - Uri inputUri = mInputUris.get(0); + FilesAdapter.ViewModel model = mFilesModels.get(0); String targetName = - (mEncryptFilenames ? "1" : FileHelper.getFilename(getActivity(), inputUri)) + (mEncryptFilenames ? "1" : FileHelper.getFilename(getActivity(), model.inputUri)) + (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File file = new File(inputUri.getPath()); + File file = new File(model.inputUri.getPath()); File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; File targetFile = new File(parentDir, targetName); FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file), @@ -221,40 +225,48 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } private void encryptClicked(boolean share) { - if (mInputUris.isEmpty()) { - Notify.create(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR).show(); + if (mFilesModels.isEmpty()) { + Notify.create(getActivity(), R.string.error_no_file_selected, + Notify.Style.ERROR).show(); return; } if (share) { mOutputUris.clear(); int filenameCounter = 1; - for (Uri uri : mInputUris) { + for (FilesAdapter.ViewModel model : mFilesModels) { String targetName = - (mEncryptFilenames ? String.valueOf(filenameCounter) : FileHelper.getFilename(getActivity(), uri)) + (mEncryptFilenames ? String.valueOf(filenameCounter) : FileHelper.getFilename(getActivity(), model.inputUri)) + (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); mOutputUris.add(TemporaryStorageProvider.createFile(getActivity(), targetName)); filenameCounter++; } startEncrypt(true); } else { - if (mInputUris.size() > 1) { - Notify.create(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR).show(); + if (mFilesModels.size() > 1) { + Notify.create(getActivity(), R.string.error_multi_not_supported, + Notify.Style.ERROR).show(); return; } 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); + public void addFile(Intent data) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + addInputUri(data.getData()); + } else { + 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); + } + } + } else { + // fallback, try old method to get single uri + addInputUri(data.getData()); } - return true; } - return false; } @Override @@ -284,22 +296,18 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } case R.id.check_use_armor: { mUseArmor = item.isChecked(); -// notifyUpdate(); break; } case R.id.check_delete_after_encrypt: { mDeleteAfterEncrypt = item.isChecked(); -// notifyUpdate(); break; } case R.id.check_enable_compression: { mUseCompression = item.isChecked(); -// onNotifyUpdate(); break; } case R.id.check_encrypt_filenames: { mEncryptFilenames = item.isChecked(); -// onNotifyUpdate(); break; } // case R.id.check_hidden_recipients: { @@ -317,15 +325,15 @@ public class EncryptFilesFragment extends CryptoOperationFragment { protected boolean inputIsValid() { // file checks - if (mInputUris.isEmpty()) { + if (mFilesModels.isEmpty()) { Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR) .show(); return false; - } else if (mInputUris.size() > 1 && !mShareAfterEncrypt) { + } else if (mFilesModels.size() > 1 && !mShareAfterEncrypt) { Log.e(Constants.TAG, "Aborting: mInputUris.size() > 1 && !mShareAfterEncrypt"); // This should be impossible... return false; - } else if (mInputUris.size() != mOutputUris.size()) { + } else if (mFilesModels.size() != mOutputUris.size()) { Log.e(Constants.TAG, "Aborting: mInputUris.size() != mOutputUris.size()"); // This as well return false; @@ -368,8 +376,8 @@ public class EncryptFilesFragment extends CryptoOperationFragment { public void onEncryptSuccess(final SignEncryptResult result) { if (mDeleteAfterEncrypt) { - final Uri[] inputUris = mInputUris.toArray(new Uri[mInputUris.size()]); - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUris); + DeleteFileDialogFragment deleteFileDialog = + DeleteFileDialogFragment.newInstance(mFilesAdapter.getAsArrayList()); deleteFileDialog.setOnDeletedListener(new DeleteFileDialogFragment.OnDeletedListener() { @Override @@ -386,8 +394,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { }); deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog"); - mInputUris.clear(); - onNotifyUpdate(); + mFilesModels.clear(); } else { if (mShareAfterEncrypt) { // Share encrypted message/file @@ -403,7 +410,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { // fill values for this action SignEncryptParcel data = new SignEncryptParcel(); - data.addInputUris(mInputUris); + data.addInputUris(mFilesAdapter.getAsArrayList()); data.addOutputUris(mOutputUris); if (mUseCompression) { @@ -563,9 +570,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { switch (requestCode) { case REQUEST_CODE_INPUT: { if (resultCode == Activity.RESULT_OK && data != null) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || !handleClipData(data)) { - addInputUri(data.getData()); - } + addFile(data); } return; } @@ -574,7 +579,6 @@ public class EncryptFilesFragment extends CryptoOperationFragment { if (resultCode == Activity.RESULT_OK && data != null) { mOutputUris.clear(); mOutputUris.add(data.getData()); - onNotifyUpdate(); startEncrypt(false); } return; @@ -588,66 +592,167 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } } - public void onNotifyUpdate() { - // Clear cache if needed - for (Uri uri : new HashSet<>(thumbnailCache.keySet())) { - if (!mInputUris.contains(uri)) { - thumbnailCache.remove(uri); + public static class FilesAdapter extends RecyclerView.Adapter { + private Activity mActivity; + private List mDataset; + private View.OnClickListener mFooterOnClickListener; + private static final int TYPE_FOOTER = 0; + private static final int TYPE_ITEM = 1; + + public static class ViewModel { + Uri inputUri; + Bitmap thumbnail; + String filename; + long fileSize; + + ViewModel(Uri inputUri, Bitmap thumbnail, String filename, long fileSize) { + this.inputUri = inputUri; + this.thumbnail = thumbnail; + this.filename = filename; + this.fileSize = fileSize; + } + + @Override + public String toString() { + return inputUri.toString(); } } - mAdapter.notifyDataSetChanged(); - } + // Provide a reference to the views for each data item + // Complex data items may need more than one view per item, and + // you provide access to all the views for a data item in a view holder + class ViewHolder extends RecyclerView.ViewHolder { + public TextView filename; + public TextView fileSize; + public View removeButton; + public ImageView thumbnail; + + public ViewHolder(View itemView) { + super(itemView); + filename = (TextView) itemView.findViewById(R.id.filename); + fileSize = (TextView) itemView.findViewById(R.id.filesize); + removeButton = itemView.findViewById(R.id.action_remove_file_from_list); + thumbnail = (ImageView) itemView.findViewById(R.id.thumbnail); + } + } - private class SelectedFilesAdapter extends BaseAdapter { + class FooterHolder extends RecyclerView.ViewHolder { + public Button mAddButton; + + public FooterHolder(View itemView) { + super(itemView); + mAddButton = (Button) itemView.findViewById(R.id.file_list_entry_add); + } + } + + // Provide a suitable constructor (depends on the kind of dataset) + public FilesAdapter(Activity activity, List myDataset, View.OnClickListener onFooterClickListener) { + mActivity = activity; + mDataset = myDataset; + mFooterOnClickListener = onFooterClickListener; + } + + // Create new views (invoked by the layout manager) @Override - public int getCount() { - return mInputUris.size(); + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == TYPE_FOOTER) { + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.file_list_entry_add, parent, false); + return new FooterHolder(v); + } else { + //inflate your layout and pass it to view holder + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.file_list_entry, parent, false); + return new ViewHolder(v); + } } + // Replace the contents of a view (invoked by the layout manager) @Override - public Object getItem(int position) { - return mInputUris.get(position); + public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { + if (holder instanceof FooterHolder) { + FooterHolder thisHolder = (FooterHolder) holder; + thisHolder.mAddButton.setOnClickListener(mFooterOnClickListener); + } else if (holder instanceof ViewHolder) { + ViewHolder thisHolder = (ViewHolder) holder; + // - get element from your dataset at this position + // - replace the contents of the view with that element + final ViewModel model = mDataset.get(position); + + thisHolder.filename.setText(model.filename); + if (model.fileSize == -1) { + thisHolder.fileSize.setText(""); + } else { + thisHolder.fileSize.setText(FileHelper.readableFileSize(model.fileSize)); + } + thisHolder.removeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + remove(model); + } + }); + if (model.thumbnail != null) { + thisHolder.thumbnail.setImageBitmap(model.thumbnail); + } else { + thisHolder.thumbnail.setImageResource(R.drawable.ic_doc_generic_am); + } + } } + // Return the size of your dataset (invoked by the layout manager) @Override - public long getItemId(int position) { - return getItem(position).hashCode(); + public int getItemCount() { + return mDataset.size() + 1; } @Override - public View getView(final int position, View convertView, ViewGroup parent) { - Uri inputUri = mInputUris.get(position); - View view; - if (convertView == null) { - view = getActivity().getLayoutInflater().inflate(R.layout.file_list_entry, null); - } else { - view = convertView; - } - ((TextView) view.findViewById(R.id.filename)).setText(FileHelper.getFilename(getActivity(), inputUri)); - long size = FileHelper.getFileSize(getActivity(), inputUri); - if (size == -1) { - ((TextView) view.findViewById(R.id.filesize)).setText(""); + public int getItemViewType(int position) { + if (isPositionFooter(position)) { + return TYPE_FOOTER; } else { - ((TextView) view.findViewById(R.id.filesize)).setText(FileHelper.readableFileSize(size)); + return TYPE_ITEM; } - view.findViewById(R.id.action_remove_file_from_list).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - delInputUri(position); + } + + private boolean isPositionFooter(int position) { + return position == mDataset.size(); + } + + public void add(Uri inputUri) { + mDataset.add(createModel(inputUri)); + notifyItemInserted(mDataset.size() - 1); + } + + public void addAll(ArrayList inputUris) { + if (inputUris != null) { + for (Uri inputUri : inputUris) { + mDataset.add(createModel(inputUri)); } - }); - int px = FormattingUtils.dpToPx(getActivity(), 48); - if (!thumbnailCache.containsKey(inputUri)) { - thumbnailCache.put(inputUri, FileHelper.getThumbnail(getActivity(), inputUri, new Point(px, px))); } - Bitmap bitmap = thumbnailCache.get(inputUri); - if (bitmap != null) { - ((ImageView) view.findViewById(R.id.thumbnail)).setImageBitmap(bitmap); - } else { - ((ImageView) view.findViewById(R.id.thumbnail)).setImageResource(R.drawable.ic_doc_generic_am); + // TODO: notifyItemInserted? + } + + private ViewModel createModel(Uri inputUri) { + int px = FormattingUtils.dpToPx(mActivity, 48); + Bitmap thumbnail = FileHelper.getThumbnail(mActivity, inputUri, new Point(px, px)); + String filename = FileHelper.getFilename(mActivity, inputUri); + long size = FileHelper.getFileSize(mActivity, inputUri); + return new ViewModel(inputUri, thumbnail, filename, size); + } + + public void remove(ViewModel model) { + int position = mDataset.indexOf(model); + mDataset.remove(position); + notifyItemRemoved(position); + } + + public ArrayList getAsArrayList() { + ArrayList uris = new ArrayList<>(); + for (ViewModel model : mDataset) { + uris.add(model.inputUri); } - return view; + return uris; } + } } -- cgit v1.2.3 From 39b131c7e58c358adf93bb64fe297884664a4ae1 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 30 Mar 2015 23:35:32 +0200 Subject: fix Encrypt* with RequiredInputParcel --- .../keychain/ui/EncryptFilesFragment.java | 27 +--------------------- 1 file changed, 1 insertion(+), 26 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 7e4c48e10..b320c8a50 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -363,7 +363,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { public void startEncrypt(boolean share) { mShareAfterEncrypt = share; - cryptoOperation(new CryptoInputParcel()); + cryptoOperation(); } public void onEncryptSuccess(final SignEncryptResult result) { @@ -512,36 +512,11 @@ public class EncryptFilesFragment extends CryptoOperationFragment { if (message.arg1 == MessageStatus.OKAY.ordinal()) { SignEncryptResult result = message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT); - -// PgpSignEncryptResult pgpResult = result.getPending(); -// -// if (pgpResult != null && pgpResult.isPending()) { -// if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) == -// PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) { -// startPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); -// } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) == -// PgpSignEncryptResult.RESULT_PENDING_NFC) { -// -// RequiredInputParcel parcel = RequiredInputParcel.createNfcSignOperation( -// pgpResult.getNfcHash(), -// pgpResult.getNfcAlgo(), -// input.getSignatureTime()); -// startNfcSign(pgpResult.getNfcKeyId(), parcel); -// -// } else { -// throw new RuntimeException("Unhandled pending result!"); -// } -// return; -// } - if (result.success()) { onEncryptSuccess(result); } else { result.createNotify(getActivity()).show(); } - - // no matter the result, reset parameters -// mSigningKeyPassphrase = null; } } }; -- cgit v1.2.3 From b0a51e7dd30b7f921d575e1e360670cbfed7be6c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 30 Mar 2015 23:39:36 +0200 Subject: remove unused NfcActivity --- .../sufficientlysecure/keychain/ui/EncryptFilesFragment.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index b320c8a50..7c52798dd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -361,11 +361,6 @@ public class EncryptFilesFragment extends CryptoOperationFragment { return true; } - public void startEncrypt(boolean share) { - mShareAfterEncrypt = share; - cryptoOperation(); - } - public void onEncryptSuccess(final SignEncryptResult result) { if (mDeleteAfterEncrypt) { final Uri[] inputUris = mInputUris.toArray(new Uri[mInputUris.size()]); @@ -472,6 +467,11 @@ public class EncryptFilesFragment extends CryptoOperationFragment { return sendIntent; } + public void startEncrypt(boolean share) { + mShareAfterEncrypt = share; + cryptoOperation(); + } + @Override protected void cryptoOperation(CryptoInputParcel cryptoInput) { -- cgit v1.2.3 From c37e7ef2410cc09cf880f6c85168605c70d5d691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 31 Mar 2015 01:53:37 +0200 Subject: Better check if file is already added --- .../keychain/ui/EncryptFilesFragment.java | 61 +++++++++++++++------- 1 file changed, 42 insertions(+), 19 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 1a67bc8dd..f85bd707b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui; import android.app.Activity; import android.app.ProgressDialog; +import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Point; @@ -62,6 +63,7 @@ import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ShareHelper; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -194,14 +196,14 @@ public class EncryptFilesFragment extends CryptoOperationFragment { return; } - if (mFilesAdapter.getAsArrayList().contains(inputUri)) { + try { + mFilesAdapter.add(inputUri); + } catch (IOException e) { Notify.create(getActivity(), getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)), Notify.Style.ERROR).show(); return; } - - mFilesAdapter.add(inputUri); mSelectedFiles.requestFocus(); } @@ -428,7 +430,6 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } else { data.setEncryptionMasterKeyIds(mEncryptionKeyIds); data.setSignatureMasterKeyId(mSigningKeyId); -// data.setSignaturePassphrase(mSigningKeyPassphrase); } return data; } @@ -578,11 +579,32 @@ public class EncryptFilesFragment extends CryptoOperationFragment { String filename; long fileSize; - ViewModel(Uri inputUri, Bitmap thumbnail, String filename, long fileSize) { + ViewModel(Context context, Uri inputUri) { this.inputUri = inputUri; - this.thumbnail = thumbnail; - this.filename = filename; - this.fileSize = fileSize; + int px = FormattingUtils.dpToPx(context, 48); + this.thumbnail = FileHelper.getThumbnail(context, inputUri, new Point(px, px)); + this.filename = FileHelper.getFilename(context, inputUri); + this.fileSize = FileHelper.getFileSize(context, inputUri); + } + + /** + * Depends on inputUri only + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ViewModel viewModel = (ViewModel) o; + return !(inputUri != null ? !inputUri.equals(viewModel.inputUri) + : viewModel.inputUri != null); + } + + /** + * Depends on inputUri only + */ + @Override + public int hashCode() { + return inputUri != null ? inputUri.hashCode() : 0; } @Override @@ -691,28 +713,29 @@ public class EncryptFilesFragment extends CryptoOperationFragment { return position == mDataset.size(); } - public void add(Uri inputUri) { - mDataset.add(createModel(inputUri)); + public void add(Uri inputUri) throws IOException { + ViewModel newModel = new ViewModel(mActivity, inputUri); + if (mDataset.contains(newModel)) { + throw new IOException("Already added!"); + } + mDataset.add(newModel); notifyItemInserted(mDataset.size() - 1); } public void addAll(ArrayList inputUris) { if (inputUris != null) { for (Uri inputUri : inputUris) { - mDataset.add(createModel(inputUri)); + ViewModel newModel = new ViewModel(mActivity, inputUri); + if (mDataset.contains(newModel)) { + Log.e(Constants.TAG, "Skipped duplicate " + inputUri.toString()); + } else { + mDataset.add(newModel); + } } } // TODO: notifyItemInserted? } - private ViewModel createModel(Uri inputUri) { - int px = FormattingUtils.dpToPx(mActivity, 48); - Bitmap thumbnail = FileHelper.getThumbnail(mActivity, inputUri, new Point(px, px)); - String filename = FileHelper.getFilename(mActivity, inputUri); - long size = FileHelper.getFileSize(mActivity, inputUri); - return new ViewModel(inputUri, thumbnail, filename, size); - } - public void remove(ViewModel model) { int position = mDataset.indexOf(model); mDataset.remove(position); -- cgit v1.2.3 From 8e5d0d1682bf0478b2df8e44c6d184a14cbd4ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 4 Apr 2015 19:01:03 +0200 Subject: Fix nullpointer with Intent API, fix clearing of encrypt file list, notify when adding a range of input uris --- .../org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index f85bd707b..ddced7cce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -390,8 +390,6 @@ public class EncryptFilesFragment extends CryptoOperationFragment { }); deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog"); - - mFilesModels.clear(); } else { if (mShareAfterEncrypt) { // Share encrypted message/file @@ -724,6 +722,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { public void addAll(ArrayList inputUris) { if (inputUris != null) { + int startIndex = mDataset.size(); for (Uri inputUri : inputUris) { ViewModel newModel = new ViewModel(mActivity, inputUri); if (mDataset.contains(newModel)) { @@ -732,8 +731,8 @@ public class EncryptFilesFragment extends CryptoOperationFragment { mDataset.add(newModel); } } + notifyItemRangeInserted(startIndex, mDataset.size() - startIndex); } - // TODO: notifyItemInserted? } public void remove(ViewModel model) { -- cgit v1.2.3 From e332699c9c67ca29a077c500e840b6005126f92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 15 Apr 2015 10:10:53 +0200 Subject: More cleanup --- .../java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java | 1 + 1 file changed, 1 insertion(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index ddced7cce..ccb4a6355 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -53,6 +53,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; -- cgit v1.2.3