From 872491686632540d7e4196cc082a94a662204497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 17 Sep 2014 15:08:02 +0200 Subject: Result parcel refactorings (WIP) --- .../org/sufficientlysecure/keychain/Constants.java | 4 +- .../keychain/ui/DecryptFilesFragment.java | 90 ++-- .../keychain/ui/DecryptTextFragment.java | 45 +- .../keychain/ui/EncryptActivity.java | 37 ++ .../keychain/ui/EncryptFileActivity.java | 474 -------------------- .../keychain/ui/EncryptFileFragment.java | 299 ------------- .../keychain/ui/EncryptFilesActivity.java | 491 +++++++++++++++++++++ .../keychain/ui/EncryptFilesFragment.java | 298 +++++++++++++ .../keychain/ui/EncryptTextActivity.java | 54 +-- .../keychain/ui/KeyListFragment.java | 8 +- .../keychain/ui/ViewKeyMainFragment.java | 6 +- 11 files changed, 919 insertions(+), 887 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileActivity.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java (limited to 'OpenKeychain/src/main/java/org') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 8ee769fdf..51fa1795d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -22,7 +22,7 @@ import android.os.Environment; import org.spongycastle.jce.provider.BouncyCastleProvider; import org.sufficientlysecure.keychain.remote.ui.AppsListActivity; import org.sufficientlysecure.keychain.ui.DecryptActivity; -import org.sufficientlysecure.keychain.ui.EncryptFileActivity; +import org.sufficientlysecure.keychain.ui.EncryptFilesActivity; import org.sufficientlysecure.keychain.ui.EncryptTextActivity; import org.sufficientlysecure.keychain.ui.KeyListActivity; @@ -94,7 +94,7 @@ public final class Constants { public static final class DrawerItems { public static final Class KEY_LIST = KeyListActivity.class; public static final Class ENCRYPT_TEXT = EncryptTextActivity.class; - public static final Class ENCRYPT_FILE = EncryptFileActivity.class; + public static final Class ENCRYPT_FILE = EncryptFilesActivity.class; public static final Class DECRYPT = DecryptActivity.class; public static final Class REGISTERED_APPS_LIST = AppsListActivity.class; public static final Class[] ARRAY = new Class[]{ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java index f5d6eba1b..baf72bad2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java @@ -48,7 +48,7 @@ import java.io.File; public class DecryptFilesFragment extends DecryptFragment { public static final String ARG_URI = "uri"; -// public static final String ARG_FROM_VIEW_INTENT = "view_intent"; + // public static final String ARG_FROM_VIEW_INTENT = "view_intent"; public static final String ARG_OPEN_DIRECTLY = "open_directly"; private static final int REQUEST_CODE_INPUT = 0x00007003; @@ -204,20 +204,28 @@ public class DecryptFilesFragment extends DecryptFragment { // get returned data bundle Bundle returnData = message.getData(); - DecryptVerifyResult result = + DecryptVerifyResult pgpResult = returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT); - switch (result.getResult()) { - case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: - showPassphraseDialog(result.getKeyIdPassphraseNeeded()); - return; - case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) { + showPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) { showPassphraseDialog(Constants.key.symmetric); - return; + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) == + DecryptVerifyResult.RESULT_PENDING_NFC) { + // TODO + } else { + throw new RuntimeException("Unhandled pending result!"); + } + } else if (pgpResult.success()) { + // go on... + askForOutputFilename(pgpResult.getDecryptMetadata().getFilename()); + } else { + pgpResult.createNotify(getActivity()).show(); } - - // go on... - askForOutputFilename(result.getDecryptMetadata().getFilename()); } } }; @@ -284,41 +292,45 @@ public class DecryptFilesFragment extends DecryptFragment { // get returned data bundle Bundle returnData = message.getData(); - DecryptVerifyResult result = + DecryptVerifyResult pgpResult = returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT); - if (result.isPending()) { - switch (result.getResult()) { - case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: - showPassphraseDialog(result.getKeyIdPassphraseNeeded()); - return; - case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: - showPassphraseDialog(Constants.key.symmetric); - return; + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) { + showPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) { + showPassphraseDialog(Constants.key.symmetric); + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) == + DecryptVerifyResult.RESULT_PENDING_NFC) { + // TODO + } else { + throw new RuntimeException("Unhandled pending result!"); } - // error, we can't work with this! - result.createNotify(getActivity()); - return; - } + } else if (pgpResult.success()) { - // display signature result in activity - onResult(result); + // display signature result in activity + onResult(pgpResult); - if (mDeleteAfter.isChecked()) { - // Create and show dialog to delete original file - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri); - deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog"); - setInputUri(null); - } + if (mDeleteAfter.isChecked()) { + // Create and show dialog to delete original file + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri); + 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); + /* + // A future open after decryption feature + if () { + Intent viewFile = new Intent(Intent.ACTION_VIEW); + viewFile.setData(mOutputUri); + startActivity(viewFile); + } + */ + } else { + pgpResult.createNotify(getActivity()).show(); } - */ } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java index c1bc563f8..a7bd0825a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -126,34 +126,37 @@ public class DecryptTextFragment extends DecryptFragment { // get returned data bundle Bundle returnData = message.getData(); - DecryptVerifyResult result = + DecryptVerifyResult pgpResult = returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT); - if (result.isPending()) { - switch (result.getResult()) { - case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: - showPassphraseDialog(result.getKeyIdPassphraseNeeded()); - return; - case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: - showPassphraseDialog(Constants.key.symmetric); - return; + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) { + showPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) { + showPassphraseDialog(Constants.key.symmetric); + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) == + DecryptVerifyResult.RESULT_PENDING_NFC) { + // TODO + } else { + throw new RuntimeException("Unhandled pending result!"); } - // error, we can't work with this! - result.createNotify(getActivity()).show(); - return; - } + } else if (pgpResult.success()) { - byte[] decryptedMessage = returnData - .getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES); - mMessage.setText(new String(decryptedMessage)); - mMessage.setHorizontallyScrolling(false); + byte[] decryptedMessage = returnData + .getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES); + mMessage.setText(new String(decryptedMessage)); + mMessage.setHorizontallyScrolling(false); - result.createNotify(getActivity()).show(); + pgpResult.createNotify(getActivity()).show(); - // display signature result in activity - onResult(result); + // display signature result in activity + onResult(pgpResult); + } else { + pgpResult.createNotify(getActivity()).show(); + } } - } }; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java new file mode 100644 index 000000000..71edcedd0 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -0,0 +1,37 @@ +package org.sufficientlysecure.keychain.ui; + +import android.content.Intent; + +import org.sufficientlysecure.keychain.nfc.NfcActivity; + +public class EncryptActivity extends DrawerActivity { + protected void startNfcSign(String pin, byte[] hashToSign, int hashAlgo) { + Intent data = new Intent(); + + // build PendingIntent for Yubikey NFC operations + Intent intent = new Intent(this, NfcActivity.class); + intent.setAction(NfcActivity.ACTION_SIGN_HASH); + // pass params through to activity that it can be returned again later to repeat pgp operation + intent.putExtra(NfcActivity.EXTRA_DATA, data); + intent.putExtra(NfcActivity.EXTRA_PIN, pin); + + intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign); + intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + + startActivityForResult(intent, 0); + } + + protected void startPassphraseDialog(long subkeyId) { + Intent data = new Intent(); + + // build PendingIntent for Yubikey NFC operations + Intent intent = new Intent(this, PassphraseDialogActivity.class); + // pass params through to activity that it can be returned again later to repeat pgp operation + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId); + +// intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + + startActivityForResult(intent, 0); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileActivity.java deleted file mode 100644 index 6f563e446..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileActivity.java +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann - * Copyright (C) 2010-2014 Thialfihar - * - * 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 - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.support.v4.app.Fragment; -import android.view.Menu; -import android.view.MenuItem; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.api.OpenKeychainIntents; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.helper.Preferences; -import org.sufficientlysecure.keychain.helper.ShareHelper; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; -import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; -import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Notify; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -public class EncryptFileActivity extends DrawerActivity implements EncryptActivityInterface { - - /* Intents */ - public static final String ACTION_ENCRYPT_DATA = OpenKeychainIntents.ENCRYPT_DATA; - - // enables ASCII Armor for file encryption when uri is given - public static final String EXTRA_ASCII_ARMOR = OpenKeychainIntents.ENCRYPT_EXTRA_ASCII_ARMOR; - - // preselect ids, for internal use - public static final String EXTRA_SIGNATURE_KEY_ID = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_ID"; - public static final String EXTRA_ENCRYPTION_KEY_IDS = Constants.EXTRA_PREFIX + "EXTRA_ENCRYPTION_IDS"; - - // view - private int mCurrentMode = MODE_ASYMMETRIC; - - // tabs - private static final int MODE_ASYMMETRIC = 0; - private static final int MODE_SYMMETRIC = 1; - - // model used by fragments - private long mEncryptionKeyIds[] = null; - private String mEncryptionUserIds[] = null; - private long mSigningKeyId = Constants.key.none; - private String mPassphrase = ""; - private boolean mUseArmor; - private boolean mDeleteAfterEncrypt = false; - private boolean mShareAfterEncrypt = false; - private ArrayList mInputUris; - private ArrayList mOutputUris; - private String mMessage = ""; - - public boolean isModeSymmetric() { - return MODE_SYMMETRIC == mCurrentMode; - } - - @Override - public boolean isUseArmor() { - return mUseArmor; - } - - @Override - public long getSignatureKey() { - return mSigningKeyId; - } - - @Override - public long[] getEncryptionKeys() { - return mEncryptionKeyIds; - } - - @Override - public String[] getEncryptionUsers() { - return mEncryptionUserIds; - } - - @Override - public void setSignatureKey(long signatureKey) { - mSigningKeyId = signatureKey; - notifyUpdate(); - } - - @Override - public void setEncryptionKeys(long[] encryptionKeys) { - mEncryptionKeyIds = encryptionKeys; - notifyUpdate(); - } - - @Override - public void setEncryptionUsers(String[] encryptionUsers) { - mEncryptionUserIds = encryptionUsers; - notifyUpdate(); - } - - @Override - public void setPassphrase(String passphrase) { - mPassphrase = passphrase; - } - - @Override - public ArrayList getInputUris() { - if (mInputUris == null) mInputUris = new ArrayList(); - return mInputUris; - } - - @Override - public ArrayList getOutputUris() { - if (mOutputUris == null) mOutputUris = new ArrayList(); - return mOutputUris; - } - - @Override - public void setInputUris(ArrayList uris) { - mInputUris = uris; - notifyUpdate(); - } - - @Override - public void setOutputUris(ArrayList uris) { - mOutputUris = uris; - notifyUpdate(); - } - - @Override - public String getMessage() { - return mMessage; - } - - @Override - public void setMessage(String message) { - mMessage = message; - } - - @Override - public void notifyUpdate() { - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - if (fragment instanceof EncryptActivityInterface.UpdateListener) { - ((UpdateListener) fragment).onNotifyUpdate(); - } - } - } - - @Override - public void startEncrypt(boolean share) { - mShareAfterEncrypt = share; - startEncrypt(); - } - - public void startEncrypt() { - if (!inputIsValid()) { - // Notify was created by inputIsValid. - return; - } - - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(this, KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); - intent.putExtra(KeychainIntentService.EXTRA_DATA, createEncryptBundle()); - - // Message is received after encrypting is done in KeychainIntentService - KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(this, - getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - Notify.showNotify(EncryptFileActivity.this, R.string.encrypt_sign_successful, Notify.Style.INFO); - - if (mDeleteAfterEncrypt) { - for (Uri inputUri : mInputUris) { - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUri); - deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); - } - mInputUris.clear(); - notifyUpdate(); - } - - if (mShareAfterEncrypt) { - // Share encrypted message/file - startActivity(sendWithChooserExcludingEncrypt(message)); - } else { - // Save encrypted file - Notify.showNotify(EncryptFileActivity.this, - R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO); - } - } - } - }; - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(serviceHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - serviceHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - private Bundle createEncryptBundle() { - // fill values for this action - Bundle data = new Bundle(); - - data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URIS); - data.putParcelableArrayList(KeychainIntentService.ENCRYPT_INPUT_URIS, mInputUris); - - data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URIS); - data.putParcelableArrayList(KeychainIntentService.ENCRYPT_OUTPUT_URIS, mOutputUris); - - data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, - Preferences.getPreferences(this).getDefaultFileCompression()); - - // Always use armor for messages - data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, mUseArmor); - - if (isModeSymmetric()) { - Log.d(Constants.TAG, "Symmetric encryption enabled!"); - String passphrase = mPassphrase; - if (passphrase.length() == 0) { - passphrase = null; - } - data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase); - } else { - data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_MASTER_ID, mSigningKeyId); - data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds); - } - return data; - } - - /** - * Create Intent Chooser but exclude OK's EncryptActivity. - */ - private Intent sendWithChooserExcludingEncrypt(Message message) { - Intent prototype = createSendIntent(message); - 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(this).createChooserExcluding(prototype, title, blacklist); - } - - private Intent createSendIntent(Message message) { - 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("application/octet-stream"); - - if (!isModeSymmetric() && mEncryptionUserIds != null) { - Set users = new HashSet(); - for (String user : mEncryptionUserIds) { - String[] userId = KeyRing.splitUserId(user); - if (userId[1] != null) { - users.add(userId[1]); - } - } - sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); - } - return sendIntent; - } - - private boolean inputIsValid() { - // file checks - - if (mInputUris.isEmpty()) { - Notify.showNotify(this, R.string.no_file_selected, Notify.Style.ERROR); - 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 (isModeSymmetric()) { - // symmetric encryption checks - - if (mPassphrase == null) { - Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR); - return false; - } - if (mPassphrase.isEmpty()) { - Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR); - 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.showNotify(this, R.string.select_encryption_key, Notify.Style.ERROR); - return false; - } - - try { - if (mSigningKeyId != 0 && PassphraseCacheService.getCachedPassphrase(this, mSigningKeyId) == null) { - PassphraseDialogFragment.show(this, mSigningKeyId, - new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - // restart - startEncrypt(); - } - } - } - ); - - return false; - } - } catch (PassphraseCacheService.KeyNotFoundException e) { - Log.e(Constants.TAG, "Key not found!", e); - } - } - return true; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.encrypt_file_activity); - - // if called with an intent action, do not init drawer navigation - if (ACTION_ENCRYPT_DATA.equals(getIntent().getAction())) { - // lock drawer - deactivateDrawerNavigation(); - // TODO: back button to key? - } else { - activateDrawerNavigation(savedInstanceState); - } - - // Handle intent actions - handleActions(getIntent()); - updateModeFragment(); - - mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.encrypt_file_activity, menu); - menu.findItem(R.id.check_use_armor).setChecked(mUseArmor); - return super.onCreateOptionsMenu(menu); - } - - private void updateModeFragment() { - getSupportFragmentManager().beginTransaction() - .replace(R.id.encrypt_pager_mode, - mCurrentMode == MODE_SYMMETRIC - ? new EncryptSymmetricFragment() - : new EncryptAsymmetricFragment() - ) - .commitAllowingStateLoss(); - getSupportFragmentManager().executePendingTransactions(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.isCheckable()) { - item.setChecked(!item.isChecked()); - } - switch (item.getItemId()) { - case R.id.check_use_symmetric: - mCurrentMode = item.isChecked() ? MODE_SYMMETRIC : MODE_ASYMMETRIC; - updateModeFragment(); - notifyUpdate(); - break; - case R.id.check_use_armor: - mUseArmor = item.isChecked(); - notifyUpdate(); - break; - case R.id.check_delete_after_encrypt: - mDeleteAfterEncrypt = item.isChecked(); - notifyUpdate(); - break; - default: - return super.onOptionsItemSelected(item); - } - return true; - } - - /** - * Handles all actions with this intent - * - * @param intent - */ - private void handleActions(Intent intent) { - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - String type = intent.getType(); - ArrayList uris = new ArrayList(); - - if (extras == null) { - extras = new Bundle(); - } - - if (intent.getData() != null) { - uris.add(intent.getData()); - } - - /* - * Android's Action - */ - - // When sending to OpenKeychain Encrypt via share menu - if (Intent.ACTION_SEND.equals(action) && type != null) { - // Files via content provider, override uri and action - uris.clear(); - uris.add(intent.getParcelableExtra(Intent.EXTRA_STREAM)); - } - - if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) { - uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); - } - - if (extras.containsKey(EXTRA_ASCII_ARMOR)) { - mUseArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, true); - } - - // preselect keys given by intent - mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); - mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); - - // Save uris - mInputUris = uris; - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java deleted file mode 100644 index 14d3d1c4a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2014 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 - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -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.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.provider.TemporaryStorageProvider; - -import java.io.File; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -public class EncryptFileFragment extends Fragment implements EncryptActivityInterface.UpdateListener { - public static final String ARG_URIS = "uris"; - - private static final int REQUEST_CODE_INPUT = 0x00007003; - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - - private EncryptActivityInterface mEncryptInterface; - - // view - private View mAddView; - private View mShareFile; - private View mEncryptFile; - private ListView mSelectedFiles; - private SelectedFilesAdapter mAdapter = new SelectedFilesAdapter(); - private final Map thumbnailCache = new HashMap(); - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - try { - mEncryptInterface = (EncryptActivityInterface) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface"); - } - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.encrypt_file_fragment, container, false); - - mEncryptFile = view.findViewById(R.id.action_encrypt_file); - mEncryptFile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - encryptClicked(false); - } - }); - mShareFile = view.findViewById(R.id.action_encrypt_share); - mShareFile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - encryptClicked(true); - } - }); - - mAddView = inflater.inflate(R.layout.file_list_entry_add, null); - mAddView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - addInputUri(); - } - }); - mSelectedFiles = (ListView) view.findViewById(R.id.selected_files_list); - mSelectedFiles.addFooterView(mAddView); - mSelectedFiles.setAdapter(mAdapter); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - } - - private void addInputUri() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.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 addInputUri(Uri inputUri) { - if (inputUri == null) { - return; - } - - mEncryptInterface.getInputUris().add(inputUri); - mEncryptInterface.notifyUpdate(); - mSelectedFiles.requestFocus(); - - /** - * We hide the encrypt to file button if multiple files are selected. - * - * With Android L it will be possible to select a target directory for multiple files, so we might want to - * change this later - */ - - if (mEncryptInterface.getInputUris().size() > 1) { - mEncryptFile.setVisibility(View.GONE); - } else { - mEncryptFile.setVisibility(View.VISIBLE); - } - } - - private void delInputUri(int position) { - mEncryptInterface.getInputUris().remove(position); - mEncryptInterface.notifyUpdate(); - mSelectedFiles.requestFocus(); - - if (mEncryptInterface.getInputUris().size() > 1) { - mEncryptFile.setVisibility(View.GONE); - } else { - mEncryptFile.setVisibility(View.VISIBLE); - } - } - - private void showOutputFileDialog() { - if (mEncryptInterface.getInputUris().size() > 1 || mEncryptInterface.getInputUris().isEmpty()) { - throw new IllegalStateException(); - } - Uri inputUri = mEncryptInterface.getInputUris().get(0); - 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; - String targetName = FileHelper.getFilename(getActivity(), inputUri) + - (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"); - File targetFile = new File(parentDir, targetName); - FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file), - getString(R.string.specify_file_to_encrypt_to), targetFile, REQUEST_CODE_OUTPUT); - } else { - FileHelper.saveDocument(this, "*/*", FileHelper.getFilename(getActivity(), inputUri) + - (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"), REQUEST_CODE_OUTPUT); - } - } - - private void encryptClicked(boolean share) { - if (share) { - mEncryptInterface.getOutputUris().clear(); - for (Uri uri : mEncryptInterface.getInputUris()) { - String targetName = FileHelper.getFilename(getActivity(), uri) + - (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"); - mEncryptInterface.getOutputUris().add(TemporaryStorageProvider.createFile(getActivity(), targetName)); - } - 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 (Build.VERSION.SDK_INT < Build.VERSION_CODES.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; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - - break; - } - } - } - - @Override - public void onNotifyUpdate() { - // Clear cache if needed - for (Uri uri : new HashSet(thumbnailCache.keySet())) { - if (!mEncryptInterface.getInputUris().contains(uri)) { - thumbnailCache.remove(uri); - } - } - - mAdapter.notifyDataSetChanged(); - } - - private class SelectedFilesAdapter extends BaseAdapter { - @Override - public int getCount() { - return mEncryptInterface.getInputUris().size(); - } - - @Override - public Object getItem(int position) { - return mEncryptInterface.getInputUris().get(position); - } - - @Override - public long getItemId(int position) { - return getItem(position).hashCode(); - } - - @Override - public View getView(final int position, View convertView, ViewGroup parent) { - Uri inputUri = mEncryptInterface.getInputUris().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(""); - } else { - ((TextView) view.findViewById(R.id.filesize)).setText(FileHelper.readableFileSize(size)); - } - view.findViewById(R.id.action_remove_file_from_list).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - delInputUri(position); - } - }); - int px = OtherHelper.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); - } - return view; - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java new file mode 100644 index 000000000..e8a0f4689 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2010-2014 Thialfihar + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.Fragment; +import android.view.Menu; +import android.view.MenuItem; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.api.OpenKeychainIntents; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.helper.ShareHelper; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.results.SignEncryptResult; +import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +public class EncryptFilesActivity extends EncryptActivity implements EncryptActivityInterface { + + /* Intents */ + public static final String ACTION_ENCRYPT_DATA = OpenKeychainIntents.ENCRYPT_DATA; + + // enables ASCII Armor for file encryption when uri is given + public static final String EXTRA_ASCII_ARMOR = OpenKeychainIntents.ENCRYPT_EXTRA_ASCII_ARMOR; + + // preselect ids, for internal use + public static final String EXTRA_SIGNATURE_KEY_ID = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_ID"; + public static final String EXTRA_ENCRYPTION_KEY_IDS = Constants.EXTRA_PREFIX + "EXTRA_ENCRYPTION_IDS"; + + // view + private int mCurrentMode = MODE_ASYMMETRIC; + + // tabs + private static final int MODE_ASYMMETRIC = 0; + private static final int MODE_SYMMETRIC = 1; + + // model used by fragments + private long mEncryptionKeyIds[] = null; + private String mEncryptionUserIds[] = null; + private long mSigningKeyId = Constants.key.none; + private String mPassphrase = ""; + private boolean mUseArmor; + private boolean mDeleteAfterEncrypt = false; + private boolean mShareAfterEncrypt = false; + private ArrayList mInputUris; + private ArrayList mOutputUris; + private String mMessage = ""; + + public boolean isModeSymmetric() { + return MODE_SYMMETRIC == mCurrentMode; + } + + @Override + public boolean isUseArmor() { + return mUseArmor; + } + + @Override + public long getSignatureKey() { + return mSigningKeyId; + } + + @Override + public long[] getEncryptionKeys() { + return mEncryptionKeyIds; + } + + @Override + public String[] getEncryptionUsers() { + return mEncryptionUserIds; + } + + @Override + public void setSignatureKey(long signatureKey) { + mSigningKeyId = signatureKey; + notifyUpdate(); + } + + @Override + public void setEncryptionKeys(long[] encryptionKeys) { + mEncryptionKeyIds = encryptionKeys; + notifyUpdate(); + } + + @Override + public void setEncryptionUsers(String[] encryptionUsers) { + mEncryptionUserIds = encryptionUsers; + notifyUpdate(); + } + + @Override + public void setPassphrase(String passphrase) { + mPassphrase = passphrase; + } + + @Override + public ArrayList getInputUris() { + if (mInputUris == null) mInputUris = new ArrayList(); + return mInputUris; + } + + @Override + public ArrayList getOutputUris() { + if (mOutputUris == null) mOutputUris = new ArrayList(); + return mOutputUris; + } + + @Override + public void setInputUris(ArrayList uris) { + mInputUris = uris; + notifyUpdate(); + } + + @Override + public void setOutputUris(ArrayList uris) { + mOutputUris = uris; + notifyUpdate(); + } + + @Override + public String getMessage() { + return mMessage; + } + + @Override + public void setMessage(String message) { + mMessage = message; + } + + @Override + public void notifyUpdate() { + for (Fragment fragment : getSupportFragmentManager().getFragments()) { + if (fragment instanceof EncryptActivityInterface.UpdateListener) { + ((UpdateListener) fragment).onNotifyUpdate(); + } + } + } + + @Override + public void startEncrypt(boolean share) { + mShareAfterEncrypt = share; + startEncrypt(); + } + + public void startEncrypt() { + if (!inputIsValid()) { + // Notify was created by inputIsValid. + return; + } + + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); + intent.putExtra(KeychainIntentService.EXTRA_DATA, createEncryptBundle()); + + // Message is received after encrypting is done in KeychainIntentService + KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(this, + getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + Notify.showNotify(EncryptFilesActivity.this, R.string.encrypt_sign_successful, Notify.Style.INFO); + + SignEncryptResult pgpResult = + message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT); + + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == + SignEncryptResult.RESULT_PENDING_PASSPHRASE) { + startPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == + SignEncryptResult.RESULT_PENDING_NFC) { + + // use after nfc sign +//// data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime()); + startNfcSign("123456", pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); + } else { + throw new RuntimeException("Unhandled pending result!"); + } + } else if (pgpResult.success()) { + if (mDeleteAfterEncrypt) { + for (Uri inputUri : mInputUris) { + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUri); + deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); + } + mInputUris.clear(); + notifyUpdate(); + } + + if (mShareAfterEncrypt) { + // Share encrypted message/file + startActivity(sendWithChooserExcludingEncrypt(message)); + } else { + // Save encrypted file + Notify.showNotify(EncryptFilesActivity.this, + R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO); + } + } else { + pgpResult.createNotify(EncryptFilesActivity.this).show(); + } + } + } + }; + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(serviceHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + serviceHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + private Bundle createEncryptBundle() { + // fill values for this action + Bundle data = new Bundle(); + + data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URIS); + data.putParcelableArrayList(KeychainIntentService.ENCRYPT_INPUT_URIS, mInputUris); + + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URIS); + data.putParcelableArrayList(KeychainIntentService.ENCRYPT_OUTPUT_URIS, mOutputUris); + + data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, + Preferences.getPreferences(this).getDefaultFileCompression()); + + // Always use armor for messages + data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, mUseArmor); + + if (isModeSymmetric()) { + Log.d(Constants.TAG, "Symmetric encryption enabled!"); + String passphrase = mPassphrase; + if (passphrase.length() == 0) { + passphrase = null; + } + data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase); + } else { + data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_MASTER_ID, mSigningKeyId); + data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds); + } + return data; + } + + /** + * Create Intent Chooser but exclude OK's EncryptActivity. + */ + private Intent sendWithChooserExcludingEncrypt(Message message) { + Intent prototype = createSendIntent(message); + 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(this).createChooserExcluding(prototype, title, blacklist); + } + + private Intent createSendIntent(Message message) { + 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("application/octet-stream"); + + if (!isModeSymmetric() && mEncryptionUserIds != null) { + Set users = new HashSet(); + for (String user : mEncryptionUserIds) { + String[] userId = KeyRing.splitUserId(user); + if (userId[1] != null) { + users.add(userId[1]); + } + } + sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); + } + return sendIntent; + } + + private boolean inputIsValid() { + // file checks + + if (mInputUris.isEmpty()) { + Notify.showNotify(this, R.string.no_file_selected, Notify.Style.ERROR); + 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 (isModeSymmetric()) { + // symmetric encryption checks + + if (mPassphrase == null) { + Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR); + return false; + } + if (mPassphrase.isEmpty()) { + Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR); + 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.showNotify(this, R.string.select_encryption_key, Notify.Style.ERROR); + return false; + } + +// try { +// if (mSigningKeyId != 0 && PassphraseCacheService.getCachedPassphrase(this, mSigningKeyId) == null) { +// PassphraseDialogFragment.show(this, mSigningKeyId, +// new Handler() { +// @Override +// public void handleMessage(Message message) { +// if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { +// // restart +// startEncrypt(); +// } +// } +// } +// ); +// +// return false; +// } +// } catch (PassphraseCacheService.KeyNotFoundException e) { +// Log.e(Constants.TAG, "Key not found!", e); +// } + } + return true; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.encrypt_file_activity); + + // if called with an intent action, do not init drawer navigation + if (ACTION_ENCRYPT_DATA.equals(getIntent().getAction())) { + // lock drawer + deactivateDrawerNavigation(); + // TODO: back button to key? + } else { + activateDrawerNavigation(savedInstanceState); + } + + // Handle intent actions + handleActions(getIntent()); + updateModeFragment(); + + mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.encrypt_file_activity, menu); + menu.findItem(R.id.check_use_armor).setChecked(mUseArmor); + return super.onCreateOptionsMenu(menu); + } + + private void updateModeFragment() { + getSupportFragmentManager().beginTransaction() + .replace(R.id.encrypt_pager_mode, + mCurrentMode == MODE_SYMMETRIC + ? new EncryptSymmetricFragment() + : new EncryptAsymmetricFragment() + ) + .commitAllowingStateLoss(); + getSupportFragmentManager().executePendingTransactions(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.isCheckable()) { + item.setChecked(!item.isChecked()); + } + switch (item.getItemId()) { + case R.id.check_use_symmetric: + mCurrentMode = item.isChecked() ? MODE_SYMMETRIC : MODE_ASYMMETRIC; + updateModeFragment(); + notifyUpdate(); + break; + case R.id.check_use_armor: + mUseArmor = item.isChecked(); + notifyUpdate(); + break; + case R.id.check_delete_after_encrypt: + mDeleteAfterEncrypt = item.isChecked(); + notifyUpdate(); + break; + default: + return super.onOptionsItemSelected(item); + } + return true; + } + + /** + * Handles all actions with this intent + * + * @param intent + */ + private void handleActions(Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + String type = intent.getType(); + ArrayList uris = new ArrayList(); + + if (extras == null) { + extras = new Bundle(); + } + + if (intent.getData() != null) { + uris.add(intent.getData()); + } + + /* + * Android's Action + */ + + // When sending to OpenKeychain Encrypt via share menu + if (Intent.ACTION_SEND.equals(action) && type != null) { + // Files via content provider, override uri and action + uris.clear(); + uris.add(intent.getParcelableExtra(Intent.EXTRA_STREAM)); + } + + if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) { + uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + } + + if (extras.containsKey(EXTRA_ASCII_ARMOR)) { + mUseArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, true); + } + + // preselect keys given by intent + mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); + mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); + + // Save uris + mInputUris = uris; + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java new file mode 100644 index 000000000..477083903 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2014 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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.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.provider.TemporaryStorageProvider; + +import java.io.File; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +public class EncryptFilesFragment extends Fragment implements EncryptActivityInterface.UpdateListener { + public static final String ARG_URIS = "uris"; + + private static final int REQUEST_CODE_INPUT = 0x00007003; + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + + private EncryptActivityInterface mEncryptInterface; + + // view + private View mAddView; + private View mShareFile; + private View mEncryptFile; + private ListView mSelectedFiles; + private SelectedFilesAdapter mAdapter = new SelectedFilesAdapter(); + private final Map thumbnailCache = new HashMap(); + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mEncryptInterface = (EncryptActivityInterface) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface"); + } + } + + /** + * Inflate the layout for this fragment + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.encrypt_file_fragment, container, false); + + mEncryptFile = view.findViewById(R.id.action_encrypt_file); + mEncryptFile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + encryptClicked(false); + } + }); + mShareFile = view.findViewById(R.id.action_encrypt_share); + mShareFile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + encryptClicked(true); + } + }); + + mAddView = inflater.inflate(R.layout.file_list_entry_add, null); + mAddView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + addInputUri(); + } + }); + mSelectedFiles = (ListView) view.findViewById(R.id.selected_files_list); + mSelectedFiles.addFooterView(mAddView); + mSelectedFiles.setAdapter(mAdapter); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } + + 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), + "*/*", REQUEST_CODE_INPUT); + } + } + + private void addInputUri(Uri inputUri) { + if (inputUri == null) { + return; + } + + mEncryptInterface.getInputUris().add(inputUri); + mEncryptInterface.notifyUpdate(); + mSelectedFiles.requestFocus(); + + /** + * We hide the encrypt to file button if multiple files are selected. + * + * With Android L it will be possible to select a target directory for multiple files, so we might want to + * change this later + */ + + if (mEncryptInterface.getInputUris().size() > 1) { + mEncryptFile.setVisibility(View.GONE); + } else { + mEncryptFile.setVisibility(View.VISIBLE); + } + } + + private void delInputUri(int position) { + mEncryptInterface.getInputUris().remove(position); + mEncryptInterface.notifyUpdate(); + mSelectedFiles.requestFocus(); + + if (mEncryptInterface.getInputUris().size() > 1) { + mEncryptFile.setVisibility(View.GONE); + } else { + mEncryptFile.setVisibility(View.VISIBLE); + } + } + + private void showOutputFileDialog() { + if (mEncryptInterface.getInputUris().size() > 1 || mEncryptInterface.getInputUris().isEmpty()) { + throw new IllegalStateException(); + } + Uri inputUri = mEncryptInterface.getInputUris().get(0); + 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; + String targetName = FileHelper.getFilename(getActivity(), inputUri) + + (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"); + File targetFile = new File(parentDir, targetName); + FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file), + getString(R.string.specify_file_to_encrypt_to), targetFile, REQUEST_CODE_OUTPUT); + } else { + FileHelper.saveDocument(this, "*/*", FileHelper.getFilename(getActivity(), inputUri) + + (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"), REQUEST_CODE_OUTPUT); + } + } + + private void encryptClicked(boolean share) { + if (share) { + mEncryptInterface.getOutputUris().clear(); + for (Uri uri : mEncryptInterface.getInputUris()) { + String targetName = FileHelper.getFilename(getActivity(), uri) + + (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"); + mEncryptInterface.getOutputUris().add(TemporaryStorageProvider.createFile(getActivity(), targetName)); + } + 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 (Build.VERSION.SDK_INT < Build.VERSION_CODES.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; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + + break; + } + } + } + + @Override + public void onNotifyUpdate() { + // Clear cache if needed + for (Uri uri : new HashSet(thumbnailCache.keySet())) { + if (!mEncryptInterface.getInputUris().contains(uri)) { + thumbnailCache.remove(uri); + } + } + + mAdapter.notifyDataSetChanged(); + } + + private class SelectedFilesAdapter extends BaseAdapter { + @Override + public int getCount() { + return mEncryptInterface.getInputUris().size(); + } + + @Override + public Object getItem(int position) { + return mEncryptInterface.getInputUris().get(position); + } + + @Override + public long getItemId(int position) { + return getItem(position).hashCode(); + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + Uri inputUri = mEncryptInterface.getInputUris().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(""); + } else { + ((TextView) view.findViewById(R.id.filesize)).setText(FileHelper.readableFileSize(size)); + } + view.findViewById(R.id.action_remove_file_from_list).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + delInputUri(position); + } + }); + int px = OtherHelper.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); + } + return view; + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index 56a2ef233..1ac457f18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -46,7 +46,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Set; -public class EncryptTextActivity extends DrawerActivity implements EncryptActivityInterface { +public class EncryptTextActivity extends EncryptActivity implements EncryptActivityInterface { /* Intents */ public static final String ACTION_ENCRYPT_TEXT = OpenKeychainIntents.ENCRYPT_TEXT; @@ -192,39 +192,35 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - SignEncryptResult result = + SignEncryptResult pgpResult = message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT); - if (result.isPending()) { - Log.d(Constants.TAG, "result.getResult() " + result.getResult()); - if ((result.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == SignEncryptResult.RESULT_PENDING_PASSPHRASE) { - Log.d(Constants.TAG, "passp"); - startPassphraseDialog(result.getKeyIdPassphraseNeeded()); - } else if ((result.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == + startPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == SignEncryptResult.RESULT_PENDING_NFC) { - Log.d(Constants.TAG, "nfc"); // use after nfc sign //// data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, result.getNfcTimestamp().getTime()); - startNfcSign("123456", result.getNfcHash(), result.getNfcAlgo()); + startNfcSign("123456", pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); } else { throw new RuntimeException("Unhandled pending result!"); } - - } else if (result.success()) { + } else if (pgpResult.success()) { if (mShareAfterEncrypt) { // Share encrypted message/file startActivity(sendWithChooserExcludingEncrypt(message)); } else { // Copy to clipboard copyToClipboard(message); - result.createNotify(EncryptTextActivity.this).show(); + pgpResult.createNotify(EncryptTextActivity.this).show(); // Notify.showNotify(EncryptTextActivity.this, // R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO); } } else { - result.createNotify(EncryptTextActivity.this).show(); + pgpResult.createNotify(EncryptTextActivity.this).show(); } } } @@ -240,36 +236,6 @@ public class EncryptTextActivity extends DrawerActivity implements EncryptActivi startService(intent); } - private void startNfcSign(String pin, byte[] hashToSign, int hashAlgo) { - Intent data = new Intent(); - - // build PendingIntent for Yubikey NFC operations - Intent intent = new Intent(this, NfcActivity.class); - intent.setAction(NfcActivity.ACTION_SIGN_HASH); - // pass params through to activity that it can be returned again later to repeat pgp operation - intent.putExtra(NfcActivity.EXTRA_DATA, data); - intent.putExtra(NfcActivity.EXTRA_PIN, pin); - - intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign); - intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo); - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); - - startActivityForResult(intent, 0); - } - - private void startPassphraseDialog(long subkeyId) { - Intent data = new Intent(); - - // build PendingIntent for Yubikey NFC operations - Intent intent = new Intent(this, PassphraseDialogActivity.class); - // pass params through to activity that it can be returned again later to repeat pgp operation - intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId); - -// intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); - - startActivityForResult(intent, 0); - } - private Bundle createEncryptBundle() { // fill values for this action Bundle data = new Bundle(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 84f12522d..33efc549b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -35,8 +35,6 @@ import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.view.MenuItemCompat; import android.support.v4.widget.CursorAdapter; -import android.support.v4.widget.NoScrollableSwipeRefreshLayout; -import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.SearchView; @@ -394,9 +392,9 @@ public class KeyListFragment extends LoaderFragment @TargetApi(Build.VERSION_CODES.HONEYCOMB) protected void encrypt(ActionMode mode, long[] masterKeyIds) { - Intent intent = new Intent(getActivity(), EncryptFileActivity.class); - intent.setAction(EncryptFileActivity.ACTION_ENCRYPT_DATA); - intent.putExtra(EncryptFileActivity.EXTRA_ENCRYPTION_KEY_IDS, masterKeyIds); + Intent intent = new Intent(getActivity(), EncryptFilesActivity.class); + intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); + intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, masterKeyIds); // used instead of startActivity set actionbar based on callingPackage startActivityForResult(intent, 0); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index f48006f08..c20b13764 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -302,9 +302,9 @@ public class ViewKeyMainFragment extends LoaderFragment implements intent.setAction(EncryptTextActivity.ACTION_ENCRYPT_TEXT); intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); } else { - intent = new Intent(getActivity(), EncryptFileActivity.class); - intent.setAction(EncryptFileActivity.ACTION_ENCRYPT_DATA); - intent.putExtra(EncryptFileActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); + intent = new Intent(getActivity(), EncryptFilesActivity.class); + intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); + intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); } // used instead of startActivity set actionbar based on callingPackage startActivityForResult(intent, 0); -- cgit v1.2.3