From 25beeaceb59f9d536d2d9408fba47f9fc3700627 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 20 Jun 2015 03:22:26 +0200 Subject: more work on decrypt fragment ux --- .../keychain/ui/DecryptListFragment.java | 169 ++++++++++++++------- .../keychain/ui/DisplayTextActivity.java | 36 +---- .../keychain/ui/DisplayTextFragment.java | 22 +-- .../keychain/util/ShareHelper.java | 1 + OpenKeychain/src/main/res/values/strings.xml | 4 + 5 files changed, 128 insertions(+), 104 deletions(-) (limited to 'OpenKeychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index dd6de9c40..65bcd022d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -18,10 +18,8 @@ package org.sufficientlysecure.keychain.ui; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -32,11 +30,14 @@ import java.util.List; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.pm.LabeledIntent; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; +import android.os.Parcelable; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -58,14 +59,15 @@ import android.widget.ViewAnimator; import org.openintents.openpgp.OpenPgpMetadata; import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; -// this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15) import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +// this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15) import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder; import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel; import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration; @@ -122,6 +124,7 @@ public class DecryptListFragment vFilesList.addItemDecoration(new SpacesItemDecoration( FormattingUtils.dpToPx(getActivity(), 4))); vFilesList.setHasFixedSize(true); + // TODO make this a grid, for tablets! vFilesList.setLayoutManager(new LinearLayoutManager(getActivity())); vFilesList.setItemAnimator(new DefaultItemAnimator()); @@ -297,74 +300,127 @@ public class DecryptListFragment } if (result.success() && result.getDecryptMetadata() != null) { - final OpenPgpMetadata metadata = result.getDecryptMetadata(); onFileClick = new OnClickListener() { @Override public void onClick(View view) { + displayUri(uri); + } + }; + } + + mAdapter.addResult(uri, result, icon, onFileClick, onKeyClick); + + } + + public void displayUri(final Uri uri) { + Activity activity = getActivity(); + if (activity == null || mCurrentInputUri != null) { + return; + } + + final Uri outputUri = mOutputUris.get(uri); + final DecryptVerifyResult result = mAdapter.getItemResult(uri); + if (outputUri == null || result == null) { + return; + } + + final OpenPgpMetadata metadata = result.getDecryptMetadata(); + + // text/plain is a special case where we extract the uri content into + // the EXTRA_TEXT extra ourselves, and display a chooser which includes + // OpenKeychain's internal viewer + if ("text/plain".equals(metadata.getMimeType())) { + + // this is a significant i/o operation, use an asynctask + new AsyncTask() { + + @Override + protected Intent doInBackground(Void... params) { + Activity activity = getActivity(); - if (activity == null || mCurrentInputUri != null) { - return; + if (activity == null) { + return null; } - Uri outputUri = mOutputUris.get(uri); - - if ("text/plain".equals(metadata.getMimeType())) { - - Intent intent = new Intent(activity, DisplayTextActivity.class); - intent.setAction(Intent.ACTION_VIEW); - intent.putExtra(DisplayTextActivity.EXTRA_METADATA, result); - intent.setDataAndType(outputUri, "text/plain"); - - try { - - byte[] decryptedMessage; - { - InputStream in = activity.getContentResolver().openInputStream(outputUri); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buf = new byte[256]; - int read; - while ( (read = in.read(buf)) > 0) { - out.write(buf, 0, read); - } - in.close(); - out.close(); - decryptedMessage = out.toByteArray(); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.putExtra(DisplayTextActivity.EXTRA_METADATA, result); + intent.setDataAndType(outputUri, "text/plain"); + + try { + + byte[] decryptedMessage; + { + InputStream in = activity.getContentResolver().openInputStream(outputUri); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[256]; + int read; + while ( (read = in.read(buf)) > 0) { + out.write(buf, 0, read); } + in.close(); + out.close(); + decryptedMessage = out.toByteArray(); + } - String plaintext; - if (result.getCharset() != null) { - try { - plaintext = new String(decryptedMessage, result.getCharset()); - } catch (UnsupportedEncodingException e) { - // if we can't decode properly, just fall back to utf-8 - plaintext = new String(decryptedMessage); - } - } else { + String plaintext; + if (result.getCharset() != null) { + try { + plaintext = new String(decryptedMessage, result.getCharset()); + } catch (UnsupportedEncodingException e) { + // if we can't decode properly, just fall back to utf-8 plaintext = new String(decryptedMessage); } + } else { + plaintext = new String(decryptedMessage); + } - intent.putExtra(Intent.EXTRA_TEXT, plaintext); + intent.putExtra(Intent.EXTRA_TEXT, plaintext); - } catch (IOException e) { - Notify.create(activity, "error", Style.ERROR).show(); - return; - } + } catch (IOException e) { + Notify.create(activity, R.string.error_preparing_data, Style.ERROR).show(); + return null; + } - activity.startActivity(intent); + return intent; + } - } else { - Intent intent = new Intent(activity, DisplayTextActivity.class); - intent.setAction(Intent.ACTION_VIEW); - intent.putExtra(DisplayTextActivity.EXTRA_METADATA, result); - intent.setDataAndType(outputUri, metadata.getMimeType()); - activity.startActivity(intent); + @Override + protected void onPostExecute(Intent intent) { + // for result so we can possibly get a snackbar error from internal viewer + Activity activity = getActivity(); + if (intent == null || activity == null) { + return; } + LabeledIntent internalIntent = new LabeledIntent( + new Intent(intent).setClass(activity, DisplayTextActivity.class), + BuildConfig.APPLICATION_ID, R.string.view_internal, R.drawable.ic_launcher); + + Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show)); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, + new Parcelable[] { internalIntent }); + + activity.startActivity(chooserIntent); } - }; - } - mAdapter.addResult(uri, result, icon, onFileClick, onKeyClick); + }.execute(); + + + } else { + Intent intent = new Intent(activity, DisplayTextActivity.class); + intent.setAction(Intent.ACTION_VIEW); + + // put output uri as stream, and grant permission to other apps to use it + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.putExtra(Intent.EXTRA_STREAM, outputUri); + intent.setType(metadata.getMimeType()); + + // put metadata, although this is not likely to be used + intent.putExtra(DisplayTextActivity.EXTRA_METADATA, result); + + Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show)); + activity.startActivity(chooserIntent); + } } @@ -535,8 +591,11 @@ public class DecryptListFragment OpenPgpMetadata metadata = model.mResult.getDecryptMetadata(); String filename = metadata.getFilename(); - holder.vFilename.setText( - !TextUtils.isEmpty(filename) ? filename : mContext.getString(R.string.filename_unknown)); + if (TextUtils.isEmpty(filename)) { + filename = mContext.getString("text/plain".equals(metadata.getMimeType()) + ? R.string.filename_unknown_text : R.string.filename_unknown); + } + holder.vFilename.setText(filename); long size = metadata.getOriginalSize(); if (size == -1 || size == 0) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java index aacb55a58..5f04eb43b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java @@ -18,32 +18,23 @@ package org.sufficientlysecure.keychain.ui; + import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.text.TextUtils; import android.view.View; import android.widget.Toast; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.operations.results.SingletonResult; -import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.util.Log; -import java.util.regex.Matcher; - public class DisplayTextActivity extends BaseActivity { - // TODO make this only display text (maybe we need only the fragment?) - - /* Intents */ public static final String EXTRA_METADATA = OpenKeychainIntents.DECRYPT_EXTRA_METADATA; @Override @@ -71,41 +62,24 @@ public class DisplayTextActivity extends BaseActivity { * Handles all actions with this 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(); - } - if (savedInstanceState != null) { return; } Log.d(Constants.TAG, "ACTION_DECRYPT_TEXT"); - DecryptVerifyResult result = extras.getParcelable(EXTRA_METADATA); - String plaintext = extras.getString(Intent.EXTRA_TEXT); + DecryptVerifyResult result = intent.getParcelableExtra(EXTRA_METADATA); + String plaintext = intent.getStringExtra(Intent.EXTRA_TEXT); - if (plaintext != null) { + if (plaintext != null && result != null) { loadFragment(plaintext, result); } else { - Log.e(Constants.TAG, "EXTRA_TEXT does not contain PGP content!"); + Log.e(Constants.TAG, "Invalid data error!"); Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show(); finish(); } } - private void returnInvalidResult() { - SingletonResult result = new SingletonResult( - SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_NO_VALID_ENC); - Intent intent = new Intent(); - intent.putExtra(SingletonResult.EXTRA_RESULT, result); - setResult(RESULT_OK, intent); - finish(); - } - private void loadFragment(String plaintext, DecryptVerifyResult result) { // Create an instance of the fragment Fragment frag = DisplayTextFragment.newInstance(plaintext, result); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java index e252f8e75..7b3af48cc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java @@ -35,15 +35,14 @@ import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.ShareHelper; public class DisplayTextFragment extends DecryptFragment { - public static final String ARG_PLAINTEXT = "ciphertext"; - public static final String ARG_SHOW_MENU = "show_menu"; + + public static final String ARG_PLAINTEXT = "plaintext"; // view private TextView mText; - // model - private boolean mShowMenuOptions; - private String mPlaintext; + // model (no state to persist though, that's all in arguments!) + private boolean mShowMenuOptions = false; public static DisplayTextFragment newInstance(String plaintext, DecryptVerifyResult result) { DisplayTextFragment frag = new DisplayTextFragment(); @@ -90,10 +89,6 @@ public class DisplayTextFragment extends DecryptFragment { super.onCreate(savedInstanceState); setHasOptionsMenu(true); - - Bundle args = getArguments(); - mShowMenuOptions = args.getBoolean(ARG_SHOW_MENU, false); - } @Override @@ -118,15 +113,6 @@ public class DisplayTextFragment extends DecryptFragment { } - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putBoolean(ARG_SHOW_MENU, mShowMenuOptions); - // no need to save the decrypted text, it's in the textview - - } - @Override protected void onVerifyLoaded(boolean hideErrorOverlay) { mShowMenuOptions = hideErrorOverlay; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java index 120b84a3b..0297d149c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java @@ -91,6 +91,7 @@ public class ShareHelper { // Create chooser with only one Intent in it Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title); // append all other Intents + // TODO this line looks wrong?! chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{})); return chooserIntent; } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 1532ba204..cf90caa1c 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -1335,5 +1335,9 @@ "(error, empty log)" "Could not read input to decrypt!" ]]> + ]]> + Show Signed/Encrypted Content + "View in OpenKeychain" + "Error preparing data!" -- cgit v1.2.3