From 53bc417f8f77a9f92786457281d02431ef614ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 15 Sep 2014 10:19:55 +0200 Subject: New decrypt activity design (WIP), saner UTF8 decoding (replacing non-decodable characters) --- OpenKeychain/src/main/AndroidManifest.xml | 171 +--------- .../keychain/pgp/UncachedKeyRing.java | 5 +- .../keychain/pgp/UncachedPublicKey.java | 8 +- .../keychain/provider/ProviderHelper.java | 4 +- .../keychain/ui/DecryptActivity.java | 57 ++++ .../keychain/ui/DecryptFileFragment.java | 337 ------------------- .../keychain/ui/DecryptFilesActivity.java | 113 +++++++ .../keychain/ui/DecryptFilesFragment.java | 363 +++++++++++++++++++++ .../keychain/ui/DecryptMessageFragment.java | 188 ----------- .../keychain/ui/DecryptOldActivity.java | 166 ---------- .../keychain/ui/DecryptTextActivity.java | 158 +++++++++ .../keychain/ui/DecryptTextFragment.java | 171 ++++++++++ .../sufficientlysecure/keychain/util/Utf8Util.java | 53 +++ .../src/main/res/layout-large/decrypt_activity.xml | 18 + .../src/main/res/layout/decrypt_activity.xml | 11 + .../src/main/res/layout/decrypt_content.xml | 78 +++++ .../src/main/res/layout/decrypt_files_activity.xml | 14 + .../main/res/layout/decrypt_message_fragment.xml | 87 ----- .../src/main/res/layout/decrypt_text_activity.xml | 14 + .../src/main/res/layout/decrypt_text_fragment.xml | 90 +++++ .../src/main/res/layout/encrypt_file_content.xml | 2 +- .../src/main/res/layout/encrypt_text_content.xml | 2 +- OpenKeychain/src/main/res/values/strings.xml | 1 - 23 files changed, 1168 insertions(+), 943 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptOldActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Utf8Util.java create mode 100644 OpenKeychain/src/main/res/layout-large/decrypt_activity.xml create mode 100644 OpenKeychain/src/main/res/layout/decrypt_activity.xml create mode 100644 OpenKeychain/src/main/res/layout/decrypt_content.xml create mode 100644 OpenKeychain/src/main/res/layout/decrypt_files_activity.xml delete mode 100644 OpenKeychain/src/main/res/layout/decrypt_message_fragment.xml create mode 100644 OpenKeychain/src/main/res/layout/decrypt_text_activity.xml create mode 100644 OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 63ffc8acd..f684cdc6a 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -138,11 +138,6 @@ android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:label="@string/title_select_recipients" android:launchMode="singleTop" /> - + - - - - - - - - - - - - - - - - - - - + - - - @@ -235,129 +214,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -375,12 +237,6 @@ - - - - - - @@ -396,7 +252,12 @@ - + + + + + + diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index d64c3ea55..04f2c3dfb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -42,6 +42,7 @@ import org.sufficientlysecure.keychain.service.results.OperationResultParcel.Log import org.sufficientlysecure.keychain.service.results.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Utf8Util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -365,7 +366,7 @@ public class UncachedKeyRing { ArrayList processedUserIds = new ArrayList(); for (byte[] rawUserId : new IterableIterator(masterKey.getRawUserIDs())) { - String userId = Strings.fromUTF8ByteArray(rawUserId); + String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId); // check for duplicate user ids if (processedUserIds.contains(rawUserId)) { @@ -439,7 +440,7 @@ public class UncachedKeyRing { continue; } // warn user if the signature was made with bad encoding - if (!cert.verifySignature(masterKey, userId)) { + if (!Utf8Util.isValidUTF8(rawUserId)) { log.add(LogLevel.WARN, LogType.MSG_KC_UID_WARN_ENCODING, indent); } } catch (PgpGeneralException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java index e27190bc7..7f08d121e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java @@ -32,6 +32,7 @@ import org.spongycastle.util.Strings; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Utf8Util; import java.util.ArrayList; import java.util.Arrays; @@ -185,7 +186,7 @@ public class UncachedPublicKey { } } if (found != null) { - return Strings.fromUTF8ByteArray(found); + return Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(found); } else { return null; } @@ -204,8 +205,9 @@ public class UncachedPublicKey { public ArrayList getUnorderedUserIds() { ArrayList userIds = new ArrayList(); - for (String userId : new IterableIterator(mPublicKey.getUserIDs())) { - userIds.add(userId); + for (byte[] rawUserId : new IterableIterator(mPublicKey.getRawUserIDs())) { + // use our decoding method + userIds.add(Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId)); } return userIds; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 8e6a0dfa5..ed65b87bd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -65,6 +65,7 @@ import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressFixedScaler; import org.sufficientlysecure.keychain.util.ProgressScaler; +import org.sufficientlysecure.keychain.util.Utf8Util; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -437,8 +438,7 @@ public class ProviderHelper { List uids = new ArrayList(); for (byte[] rawUserId : new IterableIterator( masterKey.getUnorderedRawUserIds().iterator())) { - String userId = Strings.fromUTF8ByteArray(rawUserId); - Log.d(Constants.TAG, "userId: "+userId); + String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId); UserIdItem item = new UserIdItem(); uids.add(item); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java new file mode 100644 index 000000000..37382051b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -0,0 +1,57 @@ +/* + * 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.content.Intent; +import android.os.Bundle; +import android.view.View; + +import org.sufficientlysecure.keychain.R; + +public class DecryptActivity extends DrawerActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.decrypt_activity); + + activateDrawerNavigation(savedInstanceState); + + View actionFile = findViewById(R.id.decrypt_files); + View actionFromClipboard = findViewById(R.id.decrypt_from_clipboard); + + actionFile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent filesDecrypt = new Intent(DecryptActivity.this, DecryptFilesActivity.class); + filesDecrypt.setAction(DecryptFilesActivity.ACTION_DECRYPT_DATA_OPEN); + startActivity(filesDecrypt); + } + }); + + actionFromClipboard.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent clipboardDecrypt = new Intent(DecryptActivity.this, DecryptTextActivity.class); + clipboardDecrypt.setAction(DecryptTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD); + startActivity(clipboardDecrypt); + } + }); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java deleted file mode 100644 index 7d9b2b9b3..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ /dev/null @@ -1,337 +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.app.Activity; -import android.app.ProgressDialog; -import android.content.Intent; -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.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.FileHelper; -import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -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.io.File; - -public class DecryptFileFragment extends DecryptFragment { - public static final String ARG_URI = "uri"; - public static final String ARG_FROM_VIEW_INTENT = "view_intent"; - - private static final int REQUEST_CODE_INPUT = 0x00007003; - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - - // view - private TextView mFilename; - private CheckBox mDeleteAfter; - private View mDecryptButton; - - // model - private Uri mInputUri = null; - private Uri mOutputUri = null; - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.decrypt_file_fragment, container, false); - - mFilename = (TextView) view.findViewById(R.id.decrypt_file_filename); - mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption); - mDecryptButton = view.findViewById(R.id.decrypt_file_action_decrypt); - view.findViewById(R.id.decrypt_file_browse).setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - FileHelper.openDocument(DecryptFileFragment.this, "*/*", REQUEST_CODE_INPUT); - } else { - FileHelper.openFile(DecryptFileFragment.this, mInputUri, "*/*", - REQUEST_CODE_INPUT); - } - } - }); - mDecryptButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - decryptAction(); - } - }); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - setInputUri(getArguments().getParcelable(ARG_URI)); - } - - private void setInputUri(Uri inputUri) { - if (inputUri == null) { - mInputUri = null; - mFilename.setText(""); - return; - } - - mInputUri = inputUri; - mFilename.setText(FileHelper.getFilename(getActivity(), mInputUri)); - } - - private void decryptAction() { - if (mInputUri == null) { - Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR); - return; - } - -// askForOutputFilename(); - decryptOriginalFilename(null); - } - - private String removeEncryptedAppend(String name) { - if (name.endsWith(".asc") || name.endsWith(".gpg") || name.endsWith(".pgp")) { - return name.substring(0, name.length() - 4); - } - return name; - } - - private void askForOutputFilename(String originalFilename) { - String targetName; - if (!TextUtils.isEmpty(originalFilename)) { - targetName = originalFilename; - } else { - targetName = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri)); - } - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File file = new File(mInputUri.getPath()); - File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; - File targetFile = new File(parentDir, targetName); - FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), - getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); - } else { - FileHelper.saveDocument(this, "*/*", targetName, REQUEST_CODE_OUTPUT); - } - } - - private void decryptOriginalFilename(String passphrase) { - Log.d(Constants.TAG, "decryptOriginalFilename"); - - Intent intent = new Intent(getActivity(), KeychainIntentService.class); - - // fill values for this action - Bundle data = new Bundle(); - intent.setAction(KeychainIntentService.ACTION_DECRYPT_METADATA); - - // data - Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri); - - data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI); - data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri); - - data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI); - data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri); - - data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase); - - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Message is received after decrypting is done in KeychainIntentService - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), - getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - DecryptVerifyResult result = - returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); - - switch (result.getResult()) { - case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: - showPassphraseDialog(result.getKeyIdPassphraseNeeded()); - return; - case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: - showPassphraseDialog(Constants.key.symmetric); - return; - } - - // go on... - askForOutputFilename(result.getDecryptMetadata().getFilename()); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(getActivity()); - - // start service with intent - getActivity().startService(intent); - } - - protected void showPassphraseDialogForFilename(long keyId) { - PassphraseDialogFragment.show(getActivity(), keyId, - new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - String passphrase = - message.getData().getString(PassphraseDialogFragment.MESSAGE_DATA_PASSPHRASE); - decryptOriginalFilename(passphrase); - } - } - } - ); - } - - @Override - protected void decryptStart(String passphrase) { - Log.d(Constants.TAG, "decryptStart"); - - // Send all information needed to service to decrypt in other thread - Intent intent = new Intent(getActivity(), KeychainIntentService.class); - - // fill values for this action - Bundle data = new Bundle(); - - intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY); - - // data - Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri); - - data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI); - data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri); - - data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI); - data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri); - - data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase); - - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Message is received after decrypting is done in KeychainIntentService - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), - getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - DecryptVerifyResult result = - returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); - - if (result.isPending()) { - switch (result.getResult()) { - case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: - showPassphraseDialog(result.getKeyIdPassphraseNeeded()); - return; - case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: - showPassphraseDialog(Constants.key.symmetric); - return; - } - // error, we can't work with this! - result.createNotify(getActivity()); - return; - } - - // display signature result in activity - onResult(result); - - if (mDeleteAfter.isChecked()) { - // Create and show dialog to delete original file - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri); - deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog"); - setInputUri(null); - } - - /* - // A future open after decryption feature - if () { - Intent viewFile = new Intent(Intent.ACTION_VIEW); - viewFile.setData(mOutputUri); - startActivity(viewFile); - } - */ - } - - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(getActivity()); - - // start service with intent - getActivity().startService(intent); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case REQUEST_CODE_INPUT: { - if (resultCode == Activity.RESULT_OK && data != null) { - setInputUri(data.getData()); - } - return; - } - case REQUEST_CODE_OUTPUT: { - // This happens after output file was selected, so start our operation - if (resultCode == Activity.RESULT_OK && data != null) { - mOutputUri = data.getData(); - decryptStart(null); - } - return; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - - break; - } - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java new file mode 100644 index 000000000..9d972d8c0 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java @@ -0,0 +1,113 @@ +/* + * 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.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.api.OpenKeychainIntents; +import org.sufficientlysecure.keychain.util.Log; + +public class DecryptFilesActivity extends ActionBarActivity { + + /* Intents */ + public static final String ACTION_DECRYPT_DATA = OpenKeychainIntents.DECRYPT_DATA; + + // intern + public static final String ACTION_DECRYPT_DATA_OPEN = Constants.INTENT_PREFIX + "DECRYPT_DATA_OPEN"; + + DecryptFilesFragment mFragment; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.decrypt_files_activity); + + // Handle intent actions + handleActions(savedInstanceState, getIntent()); + } + + /** + * Handles all actions with this intent + * + * @param intent + */ + private void handleActions(Bundle savedInstanceState, Intent intent) { + String action = intent.getAction(); + String type = intent.getType(); + Uri uri = intent.getData(); + + Bundle mFileFragmentBundle = new Bundle(); + + /* + * Android's Action + */ + if (Intent.ACTION_SEND.equals(action) && type != null) { + // When sending to Keychain Decrypt via share menu + // Binary via content provider (could also be files) + // override uri to get stream from send + uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); + action = ACTION_DECRYPT_DATA; + } else if (Intent.ACTION_VIEW.equals(action)) { + // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) + + // override action + action = ACTION_DECRYPT_DATA; + } + + /** + * Main Actions + */ + if (ACTION_DECRYPT_DATA.equals(action) && uri != null) { + mFileFragmentBundle.putParcelable(DecryptFilesFragment.ARG_URI, uri); + + loadFragment(savedInstanceState, uri, false); + } else if (ACTION_DECRYPT_DATA_OPEN.equals(action)) { + loadFragment(savedInstanceState, null, true); + } else if (ACTION_DECRYPT_DATA.equals(action)) { + Log.e(Constants.TAG, + "Include an Uri with setData() in your Intent!"); + } + } + + private void loadFragment(Bundle savedInstanceState, Uri uri, boolean openDialog) { + // However, if we're being restored from a previous state, + // then we don't need to do anything and should return or else + // we could end up with overlapping fragments. + if (savedInstanceState != null) { + return; + } + + // Create an instance of the fragment + mFragment = DecryptFilesFragment.newInstance(uri, openDialog); + + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + getSupportFragmentManager().beginTransaction() + .replace(R.id.decrypt_files_fragment_container, mFragment) + .commitAllowingStateLoss(); + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java new file mode 100644 index 000000000..ccfcfc2c5 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java @@ -0,0 +1,363 @@ +/* + * 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.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; +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.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.FileHelper; +import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +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.io.File; + +public class DecryptFilesFragment extends DecryptFragment { + public static final String ARG_URI = "uri"; +// public static final String ARG_FROM_VIEW_INTENT = "view_intent"; + public static final String ARG_OPEN_DIRECTLY = "open_directly"; + + private static final int REQUEST_CODE_INPUT = 0x00007003; + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + + // view + private TextView mFilename; + private CheckBox mDeleteAfter; + private View mDecryptButton; + + // model + private Uri mInputUri = null; + private Uri mOutputUri = null; + + /** + * Creates new instance of this fragment + */ + public static DecryptFilesFragment newInstance(Uri uri, boolean openDirectly) { + DecryptFilesFragment frag = new DecryptFilesFragment(); + + Bundle args = new Bundle(); + args.putParcelable(ARG_URI, uri); +// args.putBoolean(ARG_FROM_VIEW_INTENT, fromViewIntent); + args.putBoolean(ARG_OPEN_DIRECTLY, openDirectly); + + frag.setArguments(args); + + return frag; + } + + /** + * Inflate the layout for this fragment + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.decrypt_file_fragment, container, false); + + mFilename = (TextView) view.findViewById(R.id.decrypt_file_filename); + mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption); + mDecryptButton = view.findViewById(R.id.decrypt_file_action_decrypt); + view.findViewById(R.id.decrypt_file_browse).setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT); + } else { + FileHelper.openFile(DecryptFilesFragment.this, mInputUri, "*/*", + REQUEST_CODE_INPUT); + } + } + }); + mDecryptButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + decryptAction(); + } + }); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + setInputUri(getArguments().getParcelable(ARG_URI)); + + if (getArguments().getBoolean(ARG_OPEN_DIRECTLY, false)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT); + } else { + FileHelper.openFile(DecryptFilesFragment.this, mInputUri, "*/*", + REQUEST_CODE_INPUT); + } + } + } + + private void setInputUri(Uri inputUri) { + if (inputUri == null) { + mInputUri = null; + mFilename.setText(""); + return; + } + + mInputUri = inputUri; + mFilename.setText(FileHelper.getFilename(getActivity(), mInputUri)); + } + + private void decryptAction() { + if (mInputUri == null) { + Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR); + return; + } + +// askForOutputFilename(); + decryptOriginalFilename(null); + } + + private String removeEncryptedAppend(String name) { + if (name.endsWith(".asc") || name.endsWith(".gpg") || name.endsWith(".pgp")) { + return name.substring(0, name.length() - 4); + } + return name; + } + + private void askForOutputFilename(String originalFilename) { + String targetName; + if (!TextUtils.isEmpty(originalFilename)) { + targetName = originalFilename; + } else { + targetName = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri)); + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + File file = new File(mInputUri.getPath()); + File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; + File targetFile = new File(parentDir, targetName); + FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), + getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); + } else { + FileHelper.saveDocument(this, "*/*", targetName, REQUEST_CODE_OUTPUT); + } + } + + private void decryptOriginalFilename(String passphrase) { + Log.d(Constants.TAG, "decryptOriginalFilename"); + + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + intent.setAction(KeychainIntentService.ACTION_DECRYPT_METADATA); + + // data + Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri); + + data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI); + data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri); + + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI); + data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri); + + data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after decrypting is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), + getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + DecryptVerifyResult result = + returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); + + switch (result.getResult()) { + case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: + showPassphraseDialog(result.getKeyIdPassphraseNeeded()); + return; + case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: + showPassphraseDialog(Constants.key.symmetric); + return; + } + + // go on... + askForOutputFilename(result.getDecryptMetadata().getFilename()); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + } + + protected void showPassphraseDialogForFilename(long keyId) { + PassphraseDialogFragment.show(getActivity(), keyId, + new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + String passphrase = + message.getData().getString(PassphraseDialogFragment.MESSAGE_DATA_PASSPHRASE); + decryptOriginalFilename(passphrase); + } + } + } + ); + } + + @Override + protected void decryptStart(String passphrase) { + Log.d(Constants.TAG, "decryptStart"); + + // Send all information needed to service to decrypt in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + + intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY); + + // data + Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri); + + data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI); + data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri); + + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI); + data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri); + + data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after decrypting is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), + getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + DecryptVerifyResult result = + returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); + + if (result.isPending()) { + switch (result.getResult()) { + case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: + showPassphraseDialog(result.getKeyIdPassphraseNeeded()); + return; + case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: + showPassphraseDialog(Constants.key.symmetric); + return; + } + // error, we can't work with this! + result.createNotify(getActivity()); + return; + } + + // display signature result in activity + onResult(result); + + if (mDeleteAfter.isChecked()) { + // Create and show dialog to delete original file + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri); + deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog"); + setInputUri(null); + } + + /* + // A future open after decryption feature + if () { + Intent viewFile = new Intent(Intent.ACTION_VIEW); + viewFile.setData(mOutputUri); + startActivity(viewFile); + } + */ + } + + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_INPUT: { + if (resultCode == Activity.RESULT_OK && data != null) { + setInputUri(data.getData()); + } + return; + } + case REQUEST_CODE_OUTPUT: { + // This happens after output file was selected, so start our operation + if (resultCode == Activity.RESULT_OK && data != null) { + mOutputUri = data.getData(); + decryptStart(null); + } + return; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + + break; + } + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java deleted file mode 100644 index a7a630be1..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java +++ /dev/null @@ -1,188 +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.app.ProgressDialog; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.EditText; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.pgp.PgpHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Notify; - -import java.util.regex.Matcher; - -public class DecryptMessageFragment extends DecryptFragment { - public static final String ARG_CIPHERTEXT = "ciphertext"; - - // view - private EditText mMessage; - private View mDecryptButton; - private View mDecryptFromCLipboardButton; - - // model - private String mCiphertext; - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.decrypt_message_fragment, container, false); - - mMessage = (EditText) view.findViewById(R.id.message); - mDecryptButton = view.findViewById(R.id.action_decrypt); - mDecryptFromCLipboardButton = view.findViewById(R.id.action_decrypt_from_clipboard); - mDecryptButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - decryptClicked(); - } - }); - mDecryptFromCLipboardButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - decryptFromClipboardClicked(); - } - }); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - String ciphertext = getArguments().getString(ARG_CIPHERTEXT); - if (ciphertext != null) { - mCiphertext = ciphertext; - decryptStart(null); - } - } - - private void decryptClicked() { - mCiphertext = mMessage.getText().toString(); - decryptStart(null); - } - - private void decryptFromClipboardClicked() { - CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity()); - - // only decrypt if clipboard content is available and a pgp message or cleartext signature - if (clipboardText != null) { - Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText); - if (!matcher.matches()) { - matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText); - } - if (matcher.matches()) { - mCiphertext = matcher.group(1); - decryptStart(null); - } else { - Notify.showNotify(getActivity(), R.string.error_invalid_data, Notify.Style.ERROR); - } - } else { - Notify.showNotify(getActivity(), R.string.error_invalid_data, Notify.Style.ERROR); - } - } - - @Override - protected void decryptStart(String passphrase) { - Log.d(Constants.TAG, "decryptStart"); - - // Send all information needed to service to decrypt in other thread - Intent intent = new Intent(getActivity(), KeychainIntentService.class); - - // fill values for this action - Bundle data = new Bundle(); - - intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY); - - // data - data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES); - data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes()); - data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase); - - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Message is received after encrypting is done in KeychainIntentService - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), - getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - DecryptVerifyResult result = - returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); - - if (result.isPending()) { - switch (result.getResult()) { - case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: - showPassphraseDialog(result.getKeyIdPassphraseNeeded()); - return; - case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: - showPassphraseDialog(Constants.key.symmetric); - return; - } - // error, we can't work with this! - result.createNotify(getActivity()).show(); - return; - } - - byte[] decryptedMessage = returnData - .getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES); - mMessage.setText(new String(decryptedMessage)); - mMessage.setHorizontallyScrolling(false); - - result.createNotify(getActivity()).show(); - - // display signature result in activity - onResult(result); - } - - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(getActivity()); - - // start service with intent - getActivity().startService(intent); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptOldActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptOldActivity.java deleted file mode 100644 index f6dfed5d7..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptOldActivity.java +++ /dev/null @@ -1,166 +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.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.view.PagerTabStrip; -import android.support.v4.view.ViewPager; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.api.OpenKeychainIntents; -import org.sufficientlysecure.keychain.pgp.PgpHelper; -import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; -import org.sufficientlysecure.keychain.util.Log; - -import java.util.regex.Matcher; - -public class DecryptOldActivity extends DrawerActivity { - - /* Intents */ - public static final String ACTION_DECRYPT = OpenKeychainIntents.DECRYPT; - - /* EXTRA keys for input */ - public static final String EXTRA_TEXT = OpenKeychainIntents.DECRYPT_EXTRA_TEXT; - - ViewPager mViewPager; - PagerTabStrip mPagerTabStrip; - PagerTabStripAdapter mTabsAdapter; - - Bundle mMessageFragmentBundle = new Bundle(); - Bundle mFileFragmentBundle = new Bundle(); - int mSwitchToTab = PAGER_TAB_MESSAGE; - - private static final int PAGER_TAB_MESSAGE = 0; - private static final int PAGER_TAB_FILE = 1; - - private void initView() { - mViewPager = (ViewPager) findViewById(R.id.decrypt_pager); - mPagerTabStrip = (PagerTabStrip) findViewById(R.id.decrypt_pager_tab_strip); - - mTabsAdapter = new PagerTabStripAdapter(this); - mViewPager.setAdapter(mTabsAdapter); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.decrypt_activity_old); - - initView(); - - activateDrawerNavigation(savedInstanceState); - - // Handle intent actions, maybe changes the bundles - handleActions(getIntent()); - - mTabsAdapter.addTab(DecryptMessageFragment.class, - mMessageFragmentBundle, getString(R.string.label_message)); - mTabsAdapter.addTab(DecryptFileFragment.class, - mFileFragmentBundle, getString(R.string.label_file)); - mViewPager.setCurrentItem(mSwitchToTab); - } - - - /** - * 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(); - Uri uri = intent.getData(); - - if (extras == null) { - extras = new Bundle(); - } - - /* - * Android's Action - */ - if (Intent.ACTION_SEND.equals(action) && type != null) { - // When sending to Keychain Decrypt via share menu - if ("text/plain".equals(type)) { - // Plain text - String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); - if (sharedText != null) { - // handle like normal text decryption, override action and extras to later - // executeServiceMethod ACTION_DECRYPT in main actions - extras.putString(EXTRA_TEXT, sharedText); - action = ACTION_DECRYPT; - } - } else { - // Binary via content provider (could also be files) - // override uri to get stream from send - uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); - action = ACTION_DECRYPT; - } - } else if (Intent.ACTION_VIEW.equals(action)) { - // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) - - // override action - action = ACTION_DECRYPT; - mFileFragmentBundle.putBoolean(DecryptFileFragment.ARG_FROM_VIEW_INTENT, true); - } - - String textData = extras.getString(EXTRA_TEXT); - - /** - * Main Actions - */ - if (ACTION_DECRYPT.equals(action) && textData != null) { - Log.d(Constants.TAG, "textData not null, matching text ..."); - Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(textData); - if (matcher.matches()) { - Log.d(Constants.TAG, "PGP_MESSAGE matched"); - textData = matcher.group(1); - // replace non breakable spaces - textData = textData.replaceAll("\\xa0", " "); - - mMessageFragmentBundle.putString(DecryptMessageFragment.ARG_CIPHERTEXT, textData); - mSwitchToTab = PAGER_TAB_MESSAGE; - } else { - matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(textData); - if (matcher.matches()) { - Log.d(Constants.TAG, "PGP_CLEARTEXT_SIGNATURE matched"); - textData = matcher.group(1); - // replace non breakable spaces - textData = textData.replaceAll("\\xa0", " "); - - mMessageFragmentBundle.putString(DecryptMessageFragment.ARG_CIPHERTEXT, textData); - mSwitchToTab = PAGER_TAB_MESSAGE; - } else { - Log.d(Constants.TAG, "Nothing matched!"); - } - } - } else if (ACTION_DECRYPT.equals(action) && uri != null) { - mFileFragmentBundle.putParcelable(DecryptFileFragment.ARG_URI, uri); - mSwitchToTab = PAGER_TAB_FILE; - } else if (ACTION_DECRYPT.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/DecryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java new file mode 100644 index 000000000..5d46b351b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java @@ -0,0 +1,158 @@ +/* + * 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.content.Intent; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; + +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.pgp.PgpHelper; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify; + +import java.util.regex.Matcher; + +public class DecryptTextActivity extends ActionBarActivity { + + /* Intents */ + public static final String ACTION_DECRYPT_TEXT = OpenKeychainIntents.DECRYPT_TEXT; + public static final String EXTRA_TEXT = OpenKeychainIntents.DECRYPT_EXTRA_TEXT; + + // intern + public static final String ACTION_DECRYPT_FROM_CLIPBOARD = Constants.INTENT_PREFIX + "DECRYPT_TEXT_FROM_CLIPBOARD"; + + DecryptTextFragment mFragment; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.decrypt_text_activity); + + // Handle intent actions + handleActions(savedInstanceState, getIntent()); + } + + /** + * Handles all actions with this intent + * + * @param intent + */ + private void handleActions(Bundle savedInstanceState, Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + String type = intent.getType(); + + if (extras == null) { + extras = new Bundle(); + } + + String textData = null; + + /* + * Android's Action + */ + if (Intent.ACTION_SEND.equals(action) && type != null) { + // When sending to Keychain Decrypt via share menu + if ("text/plain".equals(type)) { + // Plain text + String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); + if (sharedText != null) { + // handle like normal text decryption, override action and extras to later + // executeServiceMethod ACTION_DECRYPT_TEXT in main actions + textData = sharedText; + } + } + } + + /** + * Main Actions + */ + textData = extras.getString(EXTRA_TEXT); + if (ACTION_DECRYPT_TEXT.equals(action) && textData != null) { + Log.d(Constants.TAG, "textData not null, matching text ..."); + Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(textData); + if (matcher.matches()) { + Log.d(Constants.TAG, "PGP_MESSAGE matched"); + textData = matcher.group(1); + // replace non breakable spaces + textData = textData.replaceAll("\\xa0", " "); + } else { + matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(textData); + if (matcher.matches()) { + Log.d(Constants.TAG, "PGP_CLEARTEXT_SIGNATURE matched"); + textData = matcher.group(1); + // replace non breakable spaces + textData = textData.replaceAll("\\xa0", " "); + } else { + Notify.showNotify(this, R.string.error_invalid_data, Notify.Style.ERROR); + Log.d(Constants.TAG, "Nothing matched!"); + } + } + } else if (ACTION_DECRYPT_FROM_CLIPBOARD.equals(action)) { + CharSequence clipboardText = ClipboardReflection.getClipboardText(this); + + // only decrypt if clipboard content is available and a pgp message or cleartext signature + if (clipboardText != null) { + Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText); + if (!matcher.matches()) { + matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText); + } + if (matcher.matches()) { + textData = matcher.group(1); + } else { + Notify.showNotify(this, R.string.error_invalid_data, Notify.Style.ERROR); + } + } else { + Notify.showNotify(this, R.string.error_invalid_data, Notify.Style.ERROR); + } + } else if (ACTION_DECRYPT_TEXT.equals(action)) { + Log.e(Constants.TAG, + "Include the extra 'text' in your Intent!"); + } + + loadFragment(savedInstanceState, textData); + } + + + private void loadFragment(Bundle savedInstanceState, String ciphertext) { + // However, if we're being restored from a previous state, + // then we don't need to do anything and should return or else + // we could end up with overlapping fragments. + if (savedInstanceState != null) { + return; + } + + // Create an instance of the fragment + mFragment = DecryptTextFragment.newInstance(ciphertext); + + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + getSupportFragmentManager().beginTransaction() + .replace(R.id.decrypt_text_fragment_container, mFragment) + .commitAllowingStateLoss(); + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java new file mode 100644 index 000000000..13dab6673 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -0,0 +1,171 @@ +/* + * 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.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.util.Log; + +public class DecryptTextFragment extends DecryptFragment { + public static final String ARG_CIPHERTEXT = "ciphertext"; + +// // view + private TextView mMessage; +// private View mDecryptButton; +// private View mDecryptFromCLipboardButton; +// +// // model + private String mCiphertext; + + /** + * Creates new instance of this fragment + */ + public static DecryptTextFragment newInstance(String ciphertext) { + DecryptTextFragment frag = new DecryptTextFragment(); + + Bundle args = new Bundle(); + args.putString(ARG_CIPHERTEXT, ciphertext); + + frag.setArguments(args); + + return frag; + } + + /** + * Inflate the layout for this fragment + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.decrypt_text_fragment, container, false); + + mMessage = (TextView) view.findViewById(R.id.decrypt_text_plaintext); +// mDecryptButton = view.findViewById(R.id.action_decrypt); +// mDecryptFromCLipboardButton = view.findViewById(R.id.action_decrypt_from_clipboard); +// mDecryptButton.setOnClickListener(new OnClickListener() { +// @Override +// public void onClick(View v) { +// decryptClicked(); +// } +// }); +// mDecryptFromCLipboardButton.setOnClickListener(new OnClickListener() { +// @Override +// public void onClick(View v) { +// decryptFromClipboardClicked(); +// } +// }); + + return view; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String ciphertext = getArguments().getString(ARG_CIPHERTEXT); + if (ciphertext != null) { + mCiphertext = ciphertext; + decryptStart(null); + } + } + + @Override + protected void decryptStart(String passphrase) { + Log.d(Constants.TAG, "decryptStart"); + + // Send all information needed to service to decrypt in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + + intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY); + + // data + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES); + data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes()); + data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after encrypting is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), + getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + DecryptVerifyResult result = + returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); + + if (result.isPending()) { + switch (result.getResult()) { + case DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE: + showPassphraseDialog(result.getKeyIdPassphraseNeeded()); + return; + case DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE: + showPassphraseDialog(Constants.key.symmetric); + return; + } + // error, we can't work with this! + result.createNotify(getActivity()).show(); + return; + } + + byte[] decryptedMessage = returnData + .getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES); + mMessage.setText(new String(decryptedMessage)); + mMessage.setHorizontallyScrolling(false); + + result.createNotify(getActivity()).show(); + + // display signature result in activity + onResult(result); + } + + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Utf8Util.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Utf8Util.java new file mode 100644 index 000000000..bed3e28ed --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Utf8Util.java @@ -0,0 +1,53 @@ +/* + * 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.util; + +import org.sufficientlysecure.keychain.Constants; + +import java.nio.ByteBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CodingErrorAction; + +public class Utf8Util { + + public static boolean isValidUTF8(byte[] input) { + CharsetDecoder cs = Charset.forName("UTF-8").newDecoder(); + + try { + cs.decode(ByteBuffer.wrap(input)); + return true; + } catch (CharacterCodingException e) { + return false; + } + } + + public static String fromUTF8ByteArrayReplaceBadEncoding(byte[] input) { + final CharsetDecoder charsetDecoder = Charset.forName("UTF-8").newDecoder(); + charsetDecoder.onMalformedInput(CodingErrorAction.REPLACE); + charsetDecoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + + try { + return charsetDecoder.decode(ByteBuffer.wrap(input)).toString(); + } catch (CharacterCodingException e) { + Log.e(Constants.TAG, "Decoding failed!", e); + return charsetDecoder.replacement(); + } + } +} diff --git a/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml b/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml new file mode 100644 index 000000000..06487a982 --- /dev/null +++ b/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/decrypt_activity.xml b/OpenKeychain/src/main/res/layout/decrypt_activity.xml new file mode 100644 index 000000000..bb0e463b3 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/decrypt_activity.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/OpenKeychain/src/main/res/layout/decrypt_content.xml b/OpenKeychain/src/main/res/layout/decrypt_content.xml new file mode 100644 index 000000000..1fa6ea848 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/decrypt_content.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/decrypt_files_activity.xml b/OpenKeychain/src/main/res/layout/decrypt_files_activity.xml new file mode 100644 index 000000000..0380787db --- /dev/null +++ b/OpenKeychain/src/main/res/layout/decrypt_files_activity.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/decrypt_message_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_message_fragment.xml deleted file mode 100644 index 3b4eba3ef..000000000 --- a/OpenKeychain/src/main/res/layout/decrypt_message_fragment.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenKeychain/src/main/res/layout/decrypt_text_activity.xml b/OpenKeychain/src/main/res/layout/decrypt_text_activity.xml new file mode 100644 index 000000000..e08ecb39e --- /dev/null +++ b/OpenKeychain/src/main/res/layout/decrypt_text_activity.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml new file mode 100644 index 000000000..5e9189fd3 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenKeychain/src/main/res/layout/encrypt_file_content.xml b/OpenKeychain/src/main/res/layout/encrypt_file_content.xml index 34f6eadda..5bf652780 100644 --- a/OpenKeychain/src/main/res/layout/encrypt_file_content.xml +++ b/OpenKeychain/src/main/res/layout/encrypt_file_content.xml @@ -16,7 +16,7 @@ android:orientation="vertical" /> diff --git a/OpenKeychain/src/main/res/layout/encrypt_text_content.xml b/OpenKeychain/src/main/res/layout/encrypt_text_content.xml index ee87b8a7e..809f00204 100644 --- a/OpenKeychain/src/main/res/layout/encrypt_text_content.xml +++ b/OpenKeychain/src/main/res/layout/encrypt_text_content.xml @@ -16,7 +16,7 @@ android:orientation="vertical" /> diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index e1fcf4a0b..b4f1a2753 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -521,7 +521,6 @@ "Type text" - "Enter ciphertext here to decrypt and/or verify…" "default" -- cgit v1.2.3