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: [PATCH] Refactoring of EncryptTextActivity --- .../keychain/ui/EncryptActivity.java | 1 - .../keychain/ui/EncryptActivityInterface.java | 62 --- .../keychain/ui/EncryptFilesActivity.java | 17 +- .../keychain/ui/EncryptFilesFragment.java | 16 +- ...ava => EncryptModeAsymmetricFragment.java} | 6 +- ...java => EncryptModeSymmetricFragment.java} | 6 +- .../keychain/ui/EncryptTextActivity.java | 370 ++++-------------- .../keychain/ui/EncryptTextFragment.java | 337 +++++++++++++++- .../main/res/layout/encrypt_text_activity.xml | 7 +- 9 files changed, 418 insertions(+), 404 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/{EncryptAsymmetricFragment.java => EncryptModeAsymmetricFragment.java} (96%) rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/{EncryptSymmetricFragment.java => EncryptModeSymmetricFragment.java} (94%) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 949a595d3..a1edf808c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -37,7 +37,6 @@ import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.util.Passphrase; - public abstract class EncryptActivity extends BaseActivity { public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java deleted file mode 100644 index 2a102c6c4..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java +++ /dev/null @@ -1,62 +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.net.Uri; - -import org.sufficientlysecure.keychain.util.Passphrase; - -import java.util.ArrayList; - -public interface EncryptActivityInterface { - - public interface UpdateListener { - void onNotifyUpdate(); - } - - public boolean isUseArmor(); - public boolean isUseCompression(); - public boolean isEncryptFilenames(); - public boolean isHiddenRecipients(); - - public long getSignatureKey(); - public long[] getEncryptionKeys(); - public String[] getEncryptionUsers(); - public void setSignatureKey(long signatureKey); - public void setEncryptionKeys(long[] encryptionKeys); - public void setEncryptionUsers(String[] encryptionUsers); - - public void setPassphrase(Passphrase passphrase); - - // ArrayList on purpose as only those are parcelable - public ArrayList getInputUris(); - public ArrayList getOutputUris(); - public void setInputUris(ArrayList uris); - public void setOutputUris(ArrayList uris); - - public String getMessage(); - public void setMessage(String message); - - /** - * Call this to notify the UI for changes done on the array lists or arrays, - * automatically called if setter is used - */ - public void notifyUpdate(); - - public void startEncrypt(boolean share); -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java index 269fdde8e..64e908b1a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -18,11 +18,13 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.view.View; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -33,7 +35,7 @@ import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; public class EncryptFilesActivity extends BaseActivity implements - EncryptAsymmetricFragment.IAsymmetric, EncryptSymmetricFragment.ISymmetric, + EncryptModeAsymmetricFragment.IAsymmetric, EncryptModeSymmetricFragment.ISymmetric, EncryptFilesFragment.IMode { /* Intents */ @@ -53,6 +55,13 @@ public class EncryptFilesActivity extends BaseActivity implements public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setFullScreenDialogClose(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }, false); + // Handle intent actions handleActions(getIntent(), savedInstanceState); } @@ -101,7 +110,7 @@ public class EncryptFilesActivity extends BaseActivity implements if (savedInstanceState == null) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - mModeFragment = EncryptAsymmetricFragment.newInstance(mSigningKeyId, mEncryptionKeyIds); + mModeFragment = EncryptModeAsymmetricFragment.newInstance(mSigningKeyId, mEncryptionKeyIds); transaction.replace(R.id.encrypt_mode_container, mModeFragment, "mode"); mEncryptFragment = EncryptFilesFragment.newInstance(uris, useArmor); @@ -119,8 +128,8 @@ public class EncryptFilesActivity extends BaseActivity implements getSupportFragmentManager().beginTransaction() .replace(R.id.encrypt_mode_container, symmetric - ? EncryptSymmetricFragment.newInstance() - : EncryptAsymmetricFragment.newInstance(0, null) + ? EncryptModeSymmetricFragment.newInstance() + : EncryptModeAsymmetricFragment.newInstance(0, null) ) .commitAllowingStateLoss(); getSupportFragmentManager().executePendingTransactions(); 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 */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java similarity index 96% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java index 06c711b92..8c117deca 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java @@ -40,7 +40,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -public class EncryptAsymmetricFragment extends Fragment { +public class EncryptModeAsymmetricFragment extends Fragment { public interface IAsymmetric { @@ -74,8 +74,8 @@ public class EncryptAsymmetricFragment extends Fragment { /** * Creates new instance of this fragment */ - public static EncryptAsymmetricFragment newInstance(long signatureKey, long[] encryptionKeyIds) { - EncryptAsymmetricFragment frag = new EncryptAsymmetricFragment(); + public static EncryptModeAsymmetricFragment newInstance(long signatureKey, long[] encryptionKeyIds) { + EncryptModeAsymmetricFragment frag = new EncryptModeAsymmetricFragment(); Bundle args = new Bundle(); args.putLong(ARG_SINGATURE_KEY_ID, signatureKey); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java similarity index 94% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java index 22e116a42..48b1f4983 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java @@ -30,7 +30,7 @@ import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.Passphrase; -public class EncryptSymmetricFragment extends Fragment { +public class EncryptModeSymmetricFragment extends Fragment { public interface ISymmetric { @@ -45,8 +45,8 @@ public class EncryptSymmetricFragment extends Fragment { /** * Creates new instance of this fragment */ - public static EncryptSymmetricFragment newInstance() { - EncryptSymmetricFragment frag = new EncryptSymmetricFragment(); + public static EncryptModeSymmetricFragment newInstance() { + EncryptModeSymmetricFragment frag = new EncryptModeSymmetricFragment(); Bundle args = new Bundle(); frag.setArguments(args); 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 56f0b198e..2ffb29b09 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2012-2015 Dominik Schürmann * Copyright (C) 2010-2014 Thialfihar * * This program is free software: you can redistribute it and/or modify @@ -19,30 +19,21 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; -import android.net.Uri; import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.view.View; -import org.spongycastle.bcpg.CompressionAlgorithmTags; 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.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.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Passphrase; -import org.sufficientlysecure.keychain.util.ShareHelper; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -public class EncryptTextActivity extends EncryptActivity { +public class EncryptTextActivity extends BaseActivity implements + EncryptModeAsymmetricFragment.IAsymmetric, EncryptModeSymmetricFragment.ISymmetric, + EncryptTextFragment.IMode { /* Intents */ public static final String ACTION_ENCRYPT_TEXT = OpenKeychainIntents.ENCRYPT_TEXT; @@ -54,244 +45,22 @@ public class EncryptTextActivity extends EncryptActivity { 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_SIGNATURE_KEY_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 boolean mShareAfterEncrypt = false; - private boolean mUseCompression = true; - private boolean mHiddenRecipients = false; - - private long mEncryptionKeyIds[] = null; - private String mEncryptionUserIds[] = null; - // TODO Constants.key.none? What's wrong with a null value? - private long mSigningKeyId = Constants.key.none; - private Passphrase mPassphrase = new Passphrase(); - - private ArrayList mInputUris; - private ArrayList mOutputUris; - private String mMessage = ""; - - public boolean isModeSymmetric() { - return MODE_SYMMETRIC == mCurrentMode; - } - - public boolean isUseCompression() { - return mUseCompression; - } - - public boolean isHiddenRecipients() { - return mHiddenRecipients; - } - - public long getSignatureKey() { - return mSigningKeyId; - } - - public long[] getEncryptionKeys() { - return mEncryptionKeyIds; - } - - public String[] getEncryptionUsers() { - return mEncryptionUserIds; - } - - public void setSignatureKey(long signatureKey) { - mSigningKeyId = signatureKey; - } - - public void setEncryptionKeys(long[] encryptionKeys) { - mEncryptionKeyIds = encryptionKeys; - } - - public void setEncryptionUsers(String[] encryptionUsers) { - mEncryptionUserIds = encryptionUsers; - } - - public void setPassphrase(Passphrase passphrase) { - if (mPassphrase != null) { - mPassphrase.removeFromMemory(); - } - mPassphrase = passphrase; - } - - public ArrayList getInputUris() { - if (mInputUris == null) mInputUris = new ArrayList<>(); - return mInputUris; - } - - public ArrayList getOutputUris() { - if (mOutputUris == null) mOutputUris = new ArrayList<>(); - return mOutputUris; - } - - public void setInputUris(ArrayList uris) { - mInputUris = uris; - } - - public void setOutputUris(ArrayList uris) { - mOutputUris = uris; - } - - public String getMessage() { - return mMessage; - } - - public void setMessage(String message) { - mMessage = message; - } - - public void startEncrypt(boolean share) { - mShareAfterEncrypt = share; - startEncrypt(); - } - - @Override - protected void onEncryptSuccess(SignEncryptResult result) { - if (mShareAfterEncrypt) { - // Share encrypted message/file - startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes())); - } else { - // Copy to clipboard - copyToClipboard(result.getResultBytes()); - result.createNotify(EncryptTextActivity.this).show(); - // Notify.create(EncryptTextActivity.this, - // R.string.encrypt_sign_clipboard_successful, Notify.Style.OK) - // .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); - } - } - - @Override - protected SignEncryptParcel createEncryptBundle() { - // fill values for this action - SignEncryptParcel data = new SignEncryptParcel(); - - data.setBytes(mMessage.getBytes()); - data.setCleartextSignature(true); - - if (mUseCompression) { - data.setCompressionId(PgpConstants.sPreferredCompressionAlgorithms.get(0)); - } else { - data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); - } - data.setHiddenRecipients(mHiddenRecipients); - data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); - data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); - - // Always use armor for messages - data.setEnableAsciiArmorOutput(true); - - if (isModeSymmetric()) { - 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; - } - - private void copyToClipboard(byte[] resultBytes) { - ClipboardReflection.copyToClipboard(this, new String(resultBytes)); - } - - /** - * Create Intent Chooser but exclude OK's EncryptActivity. - */ - private Intent sendWithChooserExcludingEncrypt(byte[] resultBytes) { - Intent prototype = createSendIntent(resultBytes); - String title = getString(R.string.title_share_message); - - // we don't want to encrypt the encrypted, no inception ;) - String[] blacklist = new String[]{ - Constants.PACKAGE_NAME + ".ui.EncryptTextActivity", - "org.thialfihar.android.apg.ui.EncryptActivity" - }; - - return new ShareHelper(this).createChooserExcluding(prototype, title, blacklist); - } - - private Intent createSendIntent(byte[] resultBytes) { - Intent sendIntent; - sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.setType(Constants.ENCRYPTED_TEXT_MIME); - sendIntent.putExtra(Intent.EXTRA_TEXT, new String(resultBytes)); - - if (!isModeSymmetric() && mEncryptionUserIds != null) { - Set users = new HashSet<>(); - for (String user : mEncryptionUserIds) { - KeyRing.UserId userId = KeyRing.splitUserId(user); - if (userId.email != null) { - users.add(userId.email); - } - } - // pass trough email addresses as extra for email applications - sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); - } - return sendIntent; - } - - protected boolean inputIsValid() { - if (mMessage == null) { - Notify.create(this, R.string.error_message, Notify.Style.ERROR) - .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); - return false; - } - - if (isModeSymmetric()) { - // symmetric encryption checks - - if (mPassphrase == null) { - Notify.create(this, R.string.passphrases_do_not_match, Notify.Style.ERROR) - .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); - return false; - } - if (mPassphrase.isEmpty()) { - Notify.create(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) - .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); - return false; - } - - } else { - // asymmetric encryption checks - - boolean gotEncryptionKeys = (mEncryptionKeyIds != null - && mEncryptionKeyIds.length > 0); - - if (!gotEncryptionKeys && mSigningKeyId == 0) { - Notify.create(this, R.string.select_encryption_or_signature_key, Notify.Style.ERROR) - .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); - return false; - } - } - return true; - } + Fragment mModeFragment; + EncryptTextFragment mEncryptFragment; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // if called with an intent action, do not init drawer navigation - if (ACTION_ENCRYPT_TEXT.equals(getIntent().getAction())) { - // lock drawer -// deactivateDrawerNavigation(); - // TODO: back button to key? - } else { -// activateDrawerNavigation(savedInstanceState); - } + setFullScreenDialogClose(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }, false); // Handle intent actions - handleActions(getIntent()); - updateModeFragment(); + handleActions(getIntent(), savedInstanceState); } @Override @@ -299,56 +68,13 @@ public class EncryptTextActivity extends EncryptActivity { setContentView(R.layout.encrypt_text_activity); } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.encrypt_text_activity, menu); - return super.onCreateOptionsMenu(menu); - } - - private void updateModeFragment() { - getSupportFragmentManager().beginTransaction() - .replace(R.id.encrypt_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(); - break; - } - case R.id.check_enable_compression: { - mUseCompression = item.isChecked(); - break; - } -// case R.id.check_hidden_recipients: { -// mHiddenRecipients = item.isChecked(); -// notifyUpdate(); -// break; -// } - default: { - return super.onOptionsItemSelected(item); - } - } - return true; - } /** * Handles all actions with this intent * * @param intent */ - private void handleActions(Intent intent) { + private void handleActions(Intent intent, Bundle savedInstanceState) { String action = intent.getAction(); Bundle extras = intent.getExtras(); String type = intent.getType(); @@ -379,19 +105,61 @@ public class EncryptTextActivity extends EncryptActivity { } String textData = extras.getString(EXTRA_TEXT); + if (ACTION_ENCRYPT_TEXT.equals(action) && textData == null) { + Log.e(Constants.TAG, "Include the extra 'text' in your Intent!"); + return; + } // preselect keys given by intent - mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); - mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); + long mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); + long[] mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); - /** - * Main Actions - */ - if (ACTION_ENCRYPT_TEXT.equals(action) && textData != null) { - mMessage = textData; - } else if (ACTION_ENCRYPT_TEXT.equals(action)) { - Log.e(Constants.TAG, "Include the extra 'text' in your Intent!"); + + if (savedInstanceState == null) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + + mModeFragment = EncryptModeAsymmetricFragment.newInstance(mSigningKeyId, mEncryptionKeyIds); + transaction.replace(R.id.encrypt_mode_container, mModeFragment, "mode"); + + mEncryptFragment = EncryptTextFragment.newInstance(textData); + transaction.replace(R.id.encrypt_text_container, mEncryptFragment, "text"); + + transaction.commit(); + + getSupportFragmentManager().executePendingTransactions(); } } + @Override + public void onModeChanged(boolean symmetric) { + // switch fragments + getSupportFragmentManager().beginTransaction() + .replace(R.id.encrypt_mode_container, + symmetric + ? EncryptModeSymmetricFragment.newInstance() + : EncryptModeAsymmetricFragment.newInstance(0, null) + ) + .commitAllowingStateLoss(); + getSupportFragmentManager().executePendingTransactions(); + } + + @Override + public void onSignatureKeyIdChanged(long signatureKeyId) { + mEncryptFragment.setSigningKeyId(signatureKeyId); + } + + @Override + public void onEncryptionKeyIdsChanged(long[] encryptionKeyIds) { + mEncryptFragment.setEncryptionKeyIds(encryptionKeyIds); + } + + @Override + public void onEncryptionUserIdsChanged(String[] encryptionUserIds) { + mEncryptFragment.setEncryptionUserIds(encryptionUserIds); + } + + @Override + public void onPassphraseChanged(Passphrase passphrase) { + mEncryptFragment.setPassphrase(passphrase); + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index 5d9994c5c..c71edcd22 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.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 @@ -18,32 +18,102 @@ package org.sufficientlysecure.keychain.ui; import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; +import android.os.Message; +import android.os.Messenger; import android.text.Editable; import android.text.TextWatcher; 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.TextView; +import org.spongycastle.bcpg.CompressionAlgorithmTags; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; +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.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.ServiceProgressHandler; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; +import org.sufficientlysecure.keychain.util.ShareHelper; + +import java.util.HashSet; +import java.util.Set; + +public class EncryptTextFragment extends CryptoOperationFragment { + + public interface IMode { + public void onModeChanged(boolean symmetric); + } -public class EncryptTextFragment extends Fragment { public static final String ARG_TEXT = "text"; + private IMode mModeInterface; + + private boolean mSymmetricMode = true; + private boolean mShareAfterEncrypt = false; + private boolean mUseCompression = true; + private boolean mHiddenRecipients = false; + + private long mEncryptionKeyIds[] = null; + private String mEncryptionUserIds[] = null; + // TODO Constants.key.none? What's wrong with a null value? + private long mSigningKeyId = Constants.key.none; + private Passphrase mPassphrase = new Passphrase(); + private String mMessage = ""; + private TextView mText; - private EncryptActivityInterface mEncryptInterface; + 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; + } + + /** + * Creates new instance of this fragment + */ + public static EncryptTextFragment newInstance(String text) { + EncryptTextFragment frag = new EncryptTextFragment(); + + Bundle args = new Bundle(); + args.putString(ARG_TEXT, text); + frag.setArguments(args); + + return frag; + } @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 implement IMode"); } } @@ -54,6 +124,9 @@ public class EncryptTextFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_text_fragment, container, false); + if (mMessage != null) { + mText.setText(mMessage); + } mText = (TextView) view.findViewById(R.id.encrypt_text_text); mText.addTextChangedListener(new TextWatcher() { @Override @@ -68,7 +141,7 @@ public class EncryptTextFragment extends Fragment { @Override public void afterTextChanged(Editable s) { - mEncryptInterface.setMessage(s.toString()); + mMessage = s.toString(); } }); @@ -78,29 +151,43 @@ public class EncryptTextFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mMessage = getArguments().getString(ARG_TEXT); setHasOptionsMenu(true); } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - String text = mEncryptInterface.getMessage(); - if (text != null) { - mText.setText(text); - } + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.encrypt_text_activity, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { + if (item.isCheckable()) { + item.setChecked(!item.isChecked()); + } switch (item.getItemId()) { + case R.id.check_use_symmetric: { + mSymmetricMode = item.isChecked(); + mModeInterface.onModeChanged(mSymmetricMode); + break; + } + case R.id.check_enable_compression: { + mUseCompression = item.isChecked(); + break; + } +// case R.id.check_hidden_recipients: { +// mHiddenRecipients = item.isChecked(); +// notifyUpdate(); +// break; +// } case R.id.encrypt_copy: { - mEncryptInterface.startEncrypt(false); + startEncrypt(false); break; } case R.id.encrypt_share: { - mEncryptInterface.startEncrypt(true); + startEncrypt(true); break; } default: { @@ -109,4 +196,222 @@ public class EncryptTextFragment extends Fragment { } return true; } + + + protected void onEncryptSuccess(SignEncryptResult result) { + if (mShareAfterEncrypt) { + // Share encrypted message/file + startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes())); + } else { + // Copy to clipboard + copyToClipboard(result.getResultBytes()); + result.createNotify(getActivity()).show(); + // Notify.create(EncryptTextActivity.this, + // R.string.encrypt_sign_clipboard_successful, Notify.Style.OK) + // .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); + } + } + + protected SignEncryptParcel createEncryptBundle() { + // fill values for this action + SignEncryptParcel data = new SignEncryptParcel(); + + data.setBytes(mMessage.getBytes()); + data.setCleartextSignature(true); + + if (mUseCompression) { + data.setCompressionId(PgpConstants.sPreferredCompressionAlgorithms.get(0)); + } else { + data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); + } + data.setHiddenRecipients(mHiddenRecipients); + data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); + data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); + + // Always use armor for messages + data.setEnableAsciiArmorOutput(true); + + 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; + } + + private void copyToClipboard(byte[] resultBytes) { + ClipboardReflection.copyToClipboard(getActivity(), new String(resultBytes)); + } + + /** + * Create Intent Chooser but exclude OK's EncryptActivity. + */ + private Intent sendWithChooserExcludingEncrypt(byte[] resultBytes) { + Intent prototype = createSendIntent(resultBytes); + String title = getString(R.string.title_share_message); + + // we don't want to encrypt the encrypted, no inception ;) + String[] blacklist = new String[]{ + Constants.PACKAGE_NAME + ".ui.EncryptTextActivity", + "org.thialfihar.android.apg.ui.EncryptActivity" + }; + + return new ShareHelper(getActivity()).createChooserExcluding(prototype, title, blacklist); + } + + private Intent createSendIntent(byte[] resultBytes) { + Intent sendIntent; + sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.setType(Constants.ENCRYPTED_TEXT_MIME); + sendIntent.putExtra(Intent.EXTRA_TEXT, new String(resultBytes)); + + 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); + } + } + // pass trough email addresses as extra for email applications + sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); + } + return sendIntent; + } + + protected boolean inputIsValid() { + if (mMessage == null) { + Notify.create(getActivity(), R.string.error_message, Notify.Style.ERROR) + .show(this); + 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); + + if (!gotEncryptionKeys && mSigningKeyId == 0) { + Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR) + .show(this); + return false; + } + } + return true; + } + + + public void startEncrypt(boolean share) { + mShareAfterEncrypt = share; + startEncrypt(); + } + + public void startEncrypt() { + cryptoOperation(null); + } + + @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, + 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); + } + } diff --git a/OpenKeychain/src/main/res/layout/encrypt_text_activity.xml b/OpenKeychain/src/main/res/layout/encrypt_text_activity.xml index dcf5bd041..a5082010e 100644 --- a/OpenKeychain/src/main/res/layout/encrypt_text_activity.xml +++ b/OpenKeychain/src/main/res/layout/encrypt_text_activity.xml @@ -23,14 +23,13 @@ -