From 2ead78a1e51c71911d1205b4fe946c38924db145 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 1 Sep 2014 23:25:03 +0200 Subject: split EncryptActivity into Encrypt{Text,File}Activity --- .../org/sufficientlysecure/keychain/Constants.java | 9 +- .../keychain/ui/DrawerActivity.java | 3 +- .../keychain/ui/EncryptActivity.java | 626 --------------------- .../keychain/ui/EncryptFileActivity.java | 550 ++++++++++++++++++ .../keychain/ui/EncryptMessageFragment.java | 2 +- .../keychain/ui/EncryptTextActivity.java | 477 ++++++++++++++++ .../keychain/ui/KeyListFragment.java | 7 +- .../keychain/ui/ViewKeyMainFragment.java | 10 +- 8 files changed, 1042 insertions(+), 642 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index d8dad2517..7a6bd89be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -22,7 +22,8 @@ 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.EncryptActivity; +import org.sufficientlysecure.keychain.ui.EncryptFileActivity; +import org.sufficientlysecure.keychain.ui.EncryptTextActivity; import org.sufficientlysecure.keychain.ui.KeyListActivity; import java.io.File; @@ -84,12 +85,14 @@ public final class Constants { public static final class DrawerItems { public static final Class KEY_LIST = KeyListActivity.class; - public static final Class ENCRYPT = EncryptActivity.class; + public static final Class ENCRYPT_FILE = EncryptFileActivity.class; + public static final Class ENCRYPT_TEXT = EncryptTextActivity.class; public static final Class DECRYPT = DecryptActivity.class; public static final Class REGISTERED_APPS_LIST = AppsListActivity.class; public static final Class[] ARRAY = new Class[]{ KEY_LIST, - ENCRYPT, + ENCRYPT_FILE, + ENCRYPT_TEXT, DECRYPT, REGISTERED_APPS_LIST }; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java index 0e8d6f39d..b9251cad1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java @@ -79,7 +79,8 @@ public class DrawerActivity extends ActionBarActivity { NavItem mItemIconTexts[] = new NavItem[]{ new NavItem(R.drawable.ic_action_person, getString(R.string.nav_keys)), - new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt)), + new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_files)), + new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_text)), new NavItem(R.drawable.ic_action_not_secure, getString(R.string.nav_decrypt)), new NavItem(R.drawable.ic_action_view_as_list, getString(R.string.nav_apps))}; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java deleted file mode 100644 index 4cd694d48..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ /dev/null @@ -1,626 +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.content.pm.LabeledIntent; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.os.Parcelable; -import android.support.v4.app.Fragment; -import android.support.v4.view.PagerTabStrip; -import android.support.v4.view.ViewPager; -import android.view.Menu; -import android.view.MenuItem; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.helper.Preferences; -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.adapter.PagerTabStripAdapter; -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.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class EncryptActivity extends DrawerActivity implements EncryptActivityInterface { - - /* Intents */ - public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT"; - - /* EXTRA keys for input */ - public static final String EXTRA_TEXT = "text"; - - // enables ASCII Armor for file encryption when uri is given - public static final String EXTRA_ASCII_ARMOR = "ascii_armor"; - - // preselect ids, for internal use - public static final String EXTRA_SIGNATURE_KEY_ID = "signature_key_id"; - public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_key_ids"; - - // view - private int mCurrentMode = PAGER_MODE_ASYMMETRIC; - private ViewPager mViewPagerContent; - private PagerTabStrip mPagerTabStripContent; - private PagerTabStripAdapter mTabsAdapterContent; - - // tabs - private int mSwitchToContent = PAGER_CONTENT_MESSAGE; - - private static final int PAGER_MODE_ASYMMETRIC = 0; - private static final int PAGER_MODE_SYMMETRIC = 1; - private static final int PAGER_CONTENT_MESSAGE = 0; - private static final int PAGER_CONTENT_FILE = 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 PAGER_MODE_SYMMETRIC == mCurrentMode; - } - - public boolean isContentMessage() { - return PAGER_CONTENT_MESSAGE == mViewPagerContent.getCurrentItem(); - } - - @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_ENCRYPT_SIGN); - 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) { - if (!isContentMessage()) { - Notify.showNotify(EncryptActivity.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 if (isContentMessage()) { - // Copy to clipboard - copyToClipboard(message); - Notify.showNotify(EncryptActivity.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(); - - if (isContentMessage()) { - data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES); - data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mMessage.getBytes()); - - data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, - Preferences.getPreferences(this).getDefaultMessageCompression()); - } else { - 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 || isContentMessage()); - - 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_KEY_ID, mSigningKeyId); - data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds); - } - return data; - } - - private void copyToClipboard(Message message) { - ClipboardReflection.copyToClipboard(this, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES))); - } - - /** - * Create Intent Chooser but exclude OK's EncryptActivity. - *

- * Put together from some stackoverflow posts... - * - * @param message - * @return - */ - private Intent sendWithChooserExcludingEncrypt(Message message) { - Intent prototype = createSendIntent(message); - - String title = isContentMessage() ? getString(R.string.title_share_message) - : getString(R.string.title_share_file); - - // Disabled, produced an empty list on Huawei U8860 with Android Version 4.0.3 -// // fallback on Android 2.3, otherwise we would get weird results -// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { -// return Intent.createChooser(prototype, title); -// } -// -// // prevent recursion aka Inception :P -// String[] blacklist = new String[]{Constants.PACKAGE_NAME + ".ui.EncryptActivity"}; -// -// List targetedShareIntents = new ArrayList(); -// -// List resInfoList = getPackageManager().queryIntentActivities(prototype, 0); -// List resInfoListFiltered = new ArrayList(); -// if (!resInfoList.isEmpty()) { -// for (ResolveInfo resolveInfo : resInfoList) { -// // do not add blacklisted ones -// if (resolveInfo.activityInfo == null || Arrays.asList(blacklist).contains(resolveInfo.activityInfo.name)) -// continue; -// -// resInfoListFiltered.add(resolveInfo); -// } -// -// if (!resInfoListFiltered.isEmpty()) { -// // sorting for nice readability -// Collections.sort(resInfoListFiltered, new Comparator() { -// @Override -// public int compare(ResolveInfo first, ResolveInfo second) { -// String firstName = first.loadLabel(getPackageManager()).toString(); -// String secondName = second.loadLabel(getPackageManager()).toString(); -// return firstName.compareToIgnoreCase(secondName); -// } -// }); -// -// // create the custom intent list -// for (ResolveInfo resolveInfo : resInfoListFiltered) { -// Intent targetedShareIntent = (Intent) prototype.clone(); -// targetedShareIntent.setPackage(resolveInfo.activityInfo.packageName); -// targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); -// -// LabeledIntent lIntent = new LabeledIntent(targetedShareIntent, -// resolveInfo.activityInfo.packageName, -// resolveInfo.loadLabel(getPackageManager()), -// resolveInfo.activityInfo.icon); -// targetedShareIntents.add(lIntent); -// } -// -// // Create chooser with only one Intent in it -// Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title); -// // append all other Intents -// chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{})); -// return chooserIntent; -// } -// -// } - - // fallback to Android's default chooser - return Intent.createChooser(prototype, title); - } - - private Intent createSendIntent(Message message) { - Intent sendIntent; - if (isContentMessage()) { - sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.setType("text/plain"); - sendIntent.putExtra(Intent.EXTRA_TEXT, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES))); - } else { - // 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() { - if (isContentMessage()) { - if (mMessage == null) { - Notify.showNotify(this, R.string.error_message, Notify.Style.ERROR); - return false; - } - } else { - // 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 && !isContentMessage()) { - Notify.showNotify(this, R.string.select_encryption_key, Notify.Style.ERROR); - return false; - } - - if (!gotEncryptionKeys && mSigningKeyId == 0) { - Notify.showNotify(this, R.string.select_encryption_or_signature_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; - } - - private void initView() { - mViewPagerContent = (ViewPager) findViewById(R.id.encrypt_pager_content); - mPagerTabStripContent = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_content); - - mTabsAdapterContent = new PagerTabStripAdapter(this); - mViewPagerContent.setAdapter(mTabsAdapterContent); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.encrypt_activity); - - initView(); - - // if called with an intent action, do not init drawer navigation - if (ACTION_ENCRYPT.equals(getIntent().getAction())) { - // TODO: back button to key? - } else { - setupDrawerNavigation(savedInstanceState); - } - - // Handle intent actions - handleActions(getIntent()); - updateModeFragment(); - - mTabsAdapterContent.addTab(EncryptMessageFragment.class, null, getString(R.string.label_message)); - mTabsAdapterContent.addTab(EncryptFileFragment.class, null, getString(R.string.label_files)); - mViewPagerContent.setCurrentItem(mSwitchToContent); - - mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.encrypt_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 == PAGER_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() ? PAGER_MODE_SYMMETRIC : PAGER_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 - */ - if (Intent.ACTION_SEND.equals(action) && type != null) { - // When sending to OpenKeychain Encrypt via share menu - if ("text/plain".equals(type)) { - // Plain text - String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); - if (sharedText != null) { - // handle like normal text encryption, override action and extras to later - // executeServiceMethod ACTION_ENCRYPT in main actions - extras.putString(EXTRA_TEXT, sharedText); - extras.putBoolean(EXTRA_ASCII_ARMOR, true); - action = ACTION_ENCRYPT; - } - } else { - // Files via content provider, override uri and action - uris.clear(); - uris.add(intent.getParcelableExtra(Intent.EXTRA_STREAM)); - action = ACTION_ENCRYPT; - } - } - - if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) { - uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); - action = ACTION_ENCRYPT; - } - - if (extras.containsKey(EXTRA_ASCII_ARMOR)) { - mUseArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, true); - } - - String textData = extras.getString(EXTRA_TEXT); - - mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); - mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); - - // preselect keys given by intent - mCurrentMode = PAGER_MODE_ASYMMETRIC; - - /** - * Main Actions - */ - if (ACTION_ENCRYPT.equals(action) && textData != null) { - // encrypt text based on given extra - mMessage = textData; - mSwitchToContent = PAGER_CONTENT_MESSAGE; - } else if (ACTION_ENCRYPT.equals(action) && uris != null && !uris.isEmpty()) { - // encrypt file based on Uri - mInputUris = uris; - mSwitchToContent = PAGER_CONTENT_FILE; - } else if (ACTION_ENCRYPT.equals(action)) { - Log.e(Constants.TAG, - "Include the extra 'text' or an Uri with setData() in your Intent!"); - } - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileActivity.java new file mode 100644 index 000000000..8378d0569 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileActivity.java @@ -0,0 +1,550 @@ +/* + * 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.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.helper.Preferences; +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_FILE = Constants.INTENT_PREFIX + "ENCRYPT_FILE"; + + // enables ASCII Armor for file encryption when uri is given + public static final String EXTRA_ASCII_ARMOR = "ascii_armor"; + + // preselect ids, for internal use + public static final String EXTRA_SIGNATURE_KEY_ID = "signature_key_id"; + public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_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 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_ENCRYPT_SIGN); + 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 { + // Copy to clipboard + copyToClipboard(message); + 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_KEY_ID, mSigningKeyId); + data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds); + } + return data; + } + + private void copyToClipboard(Message message) { + ClipboardReflection.copyToClipboard(this, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES))); + } + + /** + * Create Intent Chooser but exclude OK's EncryptActivity. + *

+ * Put together from some stackoverflow posts... + * + * @param message + * @return + */ + private Intent sendWithChooserExcludingEncrypt(Message message) { + Intent prototype = createSendIntent(message); + + String title = getString(R.string.title_share_file); + + // Disabled, produced an empty list on Huawei U8860 with Android Version 4.0.3 +// // fallback on Android 2.3, otherwise we would get weird results +// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { +// return Intent.createChooser(prototype, title); +// } +// +// // prevent recursion aka Inception :P +// String[] blacklist = new String[]{Constants.PACKAGE_NAME + ".ui.EncryptActivity"}; +// +// List targetedShareIntents = new ArrayList(); +// +// List resInfoList = getPackageManager().queryIntentActivities(prototype, 0); +// List resInfoListFiltered = new ArrayList(); +// if (!resInfoList.isEmpty()) { +// for (ResolveInfo resolveInfo : resInfoList) { +// // do not add blacklisted ones +// if (resolveInfo.activityInfo == null || Arrays.asList(blacklist).contains(resolveInfo.activityInfo.name)) +// continue; +// +// resInfoListFiltered.add(resolveInfo); +// } +// +// if (!resInfoListFiltered.isEmpty()) { +// // sorting for nice readability +// Collections.sort(resInfoListFiltered, new Comparator() { +// @Override +// public int compare(ResolveInfo first, ResolveInfo second) { +// String firstName = first.loadLabel(getPackageManager()).toString(); +// String secondName = second.loadLabel(getPackageManager()).toString(); +// return firstName.compareToIgnoreCase(secondName); +// } +// }); +// +// // create the custom intent list +// for (ResolveInfo resolveInfo : resInfoListFiltered) { +// Intent targetedShareIntent = (Intent) prototype.clone(); +// targetedShareIntent.setPackage(resolveInfo.activityInfo.packageName); +// targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); +// +// LabeledIntent lIntent = new LabeledIntent(targetedShareIntent, +// resolveInfo.activityInfo.packageName, +// resolveInfo.loadLabel(getPackageManager()), +// resolveInfo.activityInfo.icon); +// targetedShareIntents.add(lIntent); +// } +// +// // Create chooser with only one Intent in it +// Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title); +// // append all other Intents +// chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{})); +// return chooserIntent; +// } +// +// } + + // fallback to Android's default chooser + return Intent.createChooser(prototype, title); + } + + 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; + } + + if (!gotEncryptionKeys && mSigningKeyId == 0) { + Notify.showNotify(this, R.string.select_encryption_or_signature_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_FILE.equals(getIntent().getAction())) { + // TODO: back button to key? + } else { + setupDrawerNavigation(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 + */ + + if (Intent.ACTION_SEND.equals(action) && type != null) { + // When sending to OpenKeychain Encrypt via share menu + /* if ("text/plain".equals(type)) { + // TODO handle, maybe forward to "encrypt text" activity? + + Plain text + String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); + if (sharedText != null) { + // handle like normal text encryption, override action and extras to later + // executeServiceMethod ACTION_ENCRYPT_FILE in main actions + extras.putString(EXTRA_TEXT, sharedText); + extras.putBoolean(EXTRA_ASCII_ARMOR, true); + action = ACTION_ENCRYPT_FILE; + } + + } else */ { + // 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/EncryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java index 6d753088b..7852169ec 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java @@ -54,7 +54,7 @@ public class EncryptMessageFragment extends Fragment { */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.encrypt_message_fragment, container, false); + View view = inflater.inflate(R.layout.encrypt_text_fragment, container, false); mMessage = (TextView) view.findViewById(R.id.message); mMessage.addTextChangedListener(new TextWatcher() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java new file mode 100644 index 000000000..17831b5df --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -0,0 +1,477 @@ +/* + * 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.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.helper.Preferences; +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.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 EncryptTextActivity extends DrawerActivity implements EncryptActivityInterface { + + /* Intents */ + public static final String ACTION_ENCRYPT_TEXT = Constants.INTENT_PREFIX + "ENCRYPT_TEXT"; + + /* EXTRA keys for input */ + public static final String EXTRA_TEXT = "text"; + + // preselect ids, for internal use + public static final String EXTRA_SIGNATURE_KEY_ID = "signature_key_id"; + public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_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 long mEncryptionKeyIds[] = null; + private String mEncryptionUserIds[] = null; + private long mSigningKeyId = Constants.key.none; + private String mPassphrase = ""; + 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 true; + } + + @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 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_ENCRYPT_SIGN); + 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) { + if (mShareAfterEncrypt) { + // Share encrypted message/file + startActivity(sendWithChooserExcludingEncrypt(message)); + } else { + // Copy to clipboard + copyToClipboard(message); + Notify.showNotify(EncryptTextActivity.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.TARGET, KeychainIntentService.IO_BYTES); + data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mMessage.getBytes()); + + data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, + Preferences.getPreferences(this).getDefaultMessageCompression()); + + // Always use armor for messages + data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true); + + 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_KEY_ID, mSigningKeyId); + data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds); + } + return data; + } + + private void copyToClipboard(Message message) { + ClipboardReflection.copyToClipboard(this, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES))); + } + + /** + * Create Intent Chooser but exclude OK's EncryptActivity. + *

+ * Put together from some stackoverflow posts... + * + * @param message + * @return + */ + private Intent sendWithChooserExcludingEncrypt(Message message) { + Intent prototype = createSendIntent(message); + + String title = getString(R.string.title_share_message); + + // Disabled, produced an empty list on Huawei U8860 with Android Version 4.0.3 +// // fallback on Android 2.3, otherwise we would get weird results +// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { +// return Intent.createChooser(prototype, title); +// } +// +// // prevent recursion aka Inception :P +// String[] blacklist = new String[]{Constants.PACKAGE_NAME + ".ui.EncryptActivity"}; +// +// List targetedShareIntents = new ArrayList(); +// +// List resInfoList = getPackageManager().queryIntentActivities(prototype, 0); +// List resInfoListFiltered = new ArrayList(); +// if (!resInfoList.isEmpty()) { +// for (ResolveInfo resolveInfo : resInfoList) { +// // do not add blacklisted ones +// if (resolveInfo.activityInfo == null || Arrays.asList(blacklist).contains(resolveInfo.activityInfo.name)) +// continue; +// +// resInfoListFiltered.add(resolveInfo); +// } +// +// if (!resInfoListFiltered.isEmpty()) { +// // sorting for nice readability +// Collections.sort(resInfoListFiltered, new Comparator() { +// @Override +// public int compare(ResolveInfo first, ResolveInfo second) { +// String firstName = first.loadLabel(getPackageManager()).toString(); +// String secondName = second.loadLabel(getPackageManager()).toString(); +// return firstName.compareToIgnoreCase(secondName); +// } +// }); +// +// // create the custom intent list +// for (ResolveInfo resolveInfo : resInfoListFiltered) { +// Intent targetedShareIntent = (Intent) prototype.clone(); +// targetedShareIntent.setPackage(resolveInfo.activityInfo.packageName); +// targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); +// +// LabeledIntent lIntent = new LabeledIntent(targetedShareIntent, +// resolveInfo.activityInfo.packageName, +// resolveInfo.loadLabel(getPackageManager()), +// resolveInfo.activityInfo.icon); +// targetedShareIntents.add(lIntent); +// } +// +// // Create chooser with only one Intent in it +// Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title); +// // append all other Intents +// chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{})); +// return chooserIntent; +// } +// +// } + + // fallback to Android's default chooser + return Intent.createChooser(prototype, title); + } + + private Intent createSendIntent(Message message) { + Intent sendIntent; + sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.setType("text/plain"); + sendIntent.putExtra(Intent.EXTRA_TEXT, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES))); + + 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() { + if (mMessage == null) { + Notify.showNotify(this, R.string.error_message, Notify.Style.ERROR); + 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); + + if (!gotEncryptionKeys && mSigningKeyId == 0) { + Notify.showNotify(this, R.string.select_encryption_or_signature_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_text_activity); + + // if called with an intent action, do not init drawer navigation + if (ACTION_ENCRYPT_TEXT.equals(getIntent().getAction())) { + // TODO: back button to key? + } else { + setupDrawerNavigation(savedInstanceState); + } + + // Handle intent actions + handleActions(getIntent()); + updateModeFragment(); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.encrypt_file_activity, menu); + 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; + 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(); + // Should always be text/plain + // String type = intent.getType(); + ArrayList uris = new ArrayList(); + + if (extras == null) { + extras = new Bundle(); + } + + if (intent.getData() != null) { + uris.add(intent.getData()); + } + + String textData = extras.getString(EXTRA_TEXT); + + // preselect keys given by intent + mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); + 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!"); + } + } + +} 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 b4676f9b7..5f44c61cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -37,7 +37,6 @@ import android.support.v4.view.MenuItemCompat; import android.support.v4.widget.CursorAdapter; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.SearchView; -import android.text.TextUtils; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; @@ -328,9 +327,9 @@ public class KeyListFragment extends LoaderFragment @TargetApi(Build.VERSION_CODES.HONEYCOMB) protected void encrypt(ActionMode mode, long[] masterKeyIds) { - Intent intent = new Intent(getActivity(), EncryptActivity.class); - intent.setAction(EncryptActivity.ACTION_ENCRYPT); - intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, masterKeyIds); + Intent intent = new Intent(getActivity(), EncryptFileActivity.class); + intent.setAction(EncryptFileActivity.ACTION_ENCRYPT_FILE); + intent.putExtra(EncryptFileActivity.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 c37cb014e..f8e19b209 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -23,9 +23,6 @@ import android.database.Cursor; import android.graphics.PorterDuff; 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.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; @@ -47,7 +44,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; -import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Notify; @@ -284,9 +280,9 @@ public class ViewKeyMainFragment extends LoaderFragment implements .getCachedPublicKeyRing(dataUri) .extractOrGetMasterKeyId(); long[] encryptionKeyIds = new long[]{keyId}; - Intent intent = new Intent(getActivity(), EncryptActivity.class); - intent.setAction(EncryptActivity.ACTION_ENCRYPT); - intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); + Intent intent = new Intent(getActivity(), EncryptFileActivity.class); + intent.setAction(EncryptFileActivity.ACTION_ENCRYPT_FILE); + intent.putExtra(EncryptFileActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); // used instead of startActivity set actionbar based on callingPackage startActivityForResult(intent, 0); } catch (PgpGeneralException e) { -- cgit v1.2.3