From 2663cd1e42bd5b998d30fd1bf526535a95b243ba Mon Sep 17 00:00:00 2001 From: M66B Date: Sat, 25 Aug 2018 18:51:01 +0000 Subject: [PATCH] OpenPGP improvements Refs #43 --- .../java/eu/faircode/email/ActivityView.java | 119 +++++++++--------- .../eu/faircode/email/FragmentCompose.java | 38 ++++-- .../eu/faircode/email/FragmentMessage.java | 36 ++++-- app/src/main/res/values/strings.xml | 2 + 4 files changed, 115 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index 16d0aa95a9..cb45f955de 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -86,13 +86,15 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack private BillingClient billingClient = null; private boolean newIntent = false; + private long attachment = -1; private static final int ATTACHMENT_BUFFER_SIZE = 8192; // bytes static final int REQUEST_VIEW = 1; static final int REQUEST_UNSEEN = 2; - static final int REQUEST_OPENPGP = 3; - static final int REQUEST_ATTACHMENT_OFFSET = 10; + + static final int REQUEST_ATTACHMENT = 1; + static final int REQUEST_OPENPGP = 2; static final String ACTION_VIEW_MESSAGES = BuildConfig.APPLICATION_ID + ".VIEW_MESSAGES"; static final String ACTION_VIEW_MESSAGE = BuildConfig.APPLICATION_ID + ".VIEW_MESSAGE"; @@ -705,11 +707,12 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack } private void onStoreAttachment(Intent intent) { + attachment = intent.getLongExtra("id", -1); Intent create = new Intent(Intent.ACTION_CREATE_DOCUMENT); create.addCategory(Intent.CATEGORY_OPENABLE); create.setType(intent.getStringExtra("type")); create.putExtra(Intent.EXTRA_TITLE, intent.getStringExtra("name")); - startActivityForResult(create, (int) intent.getLongExtra("id", -1) + REQUEST_ATTACHMENT_OFFSET); + startActivityForResult(create, REQUEST_ATTACHMENT); } private void onActivatePro(Intent intent) { @@ -740,67 +743,69 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (resultCode == Activity.RESULT_OK && requestCode > REQUEST_ATTACHMENT_OFFSET) { - Bundle args = new Bundle(); - args.putLong("id", requestCode - REQUEST_ATTACHMENT_OFFSET); - args.putParcelable("uri", data.getData()); - new SimpleTask() { - @Override - protected Void onLoad(Context context, Bundle args) throws Throwable { - long id = args.getLong("id"); - Uri uri = args.getParcelable("uri"); + Log.i(Helper.TAG, "View onActivityResult request=" + requestCode + " result=" + resultCode + " data=" + data); + if (resultCode == Activity.RESULT_OK) + if (requestCode == REQUEST_ATTACHMENT) { + Bundle args = new Bundle(); + args.putLong("id", attachment); + args.putParcelable("uri", data.getData()); + new SimpleTask() { + @Override + protected Void onLoad(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + Uri uri = args.getParcelable("uri"); - File file = EntityAttachment.getFile(context, id); + File file = EntityAttachment.getFile(context, id); - ParcelFileDescriptor pfd = null; - FileOutputStream fos = null; - FileInputStream fis = null; - try { - pfd = context.getContentResolver().openFileDescriptor(uri, "w"); - fos = new FileOutputStream(pfd.getFileDescriptor()); - fis = new FileInputStream(file); - - byte[] buffer = new byte[ATTACHMENT_BUFFER_SIZE]; - int read; - while ((read = fis.read(buffer)) != -1) { - fos.write(buffer, 0, read); - } - } finally { + ParcelFileDescriptor pfd = null; + FileOutputStream fos = null; + FileInputStream fis = null; try { - if (pfd != null) - pfd.close(); - } catch (Throwable ex) { - Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + pfd = context.getContentResolver().openFileDescriptor(uri, "w"); + fos = new FileOutputStream(pfd.getFileDescriptor()); + fis = new FileInputStream(file); + + byte[] buffer = new byte[ATTACHMENT_BUFFER_SIZE]; + int read; + while ((read = fis.read(buffer)) != -1) { + fos.write(buffer, 0, read); + } + } finally { + try { + if (pfd != null) + pfd.close(); + } catch (Throwable ex) { + Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + } + try { + if (fos != null) + fos.close(); + } catch (Throwable ex) { + Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + } + try { + if (fis != null) + fis.close(); + } catch (Throwable ex) { + Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + } } - try { - if (fos != null) - fos.close(); - } catch (Throwable ex) { - Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); - } - try { - if (fis != null) - fis.close(); - } catch (Throwable ex) { - Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); - } - } - return null; - } + return null; + } - @Override - protected void onLoaded(Bundle args, Void data) { - Toast.makeText(ActivityView.this, R.string.title_attachment_saved, Toast.LENGTH_LONG).show(); - } + @Override + protected void onLoaded(Bundle args, Void data) { + Toast.makeText(ActivityView.this, R.string.title_attachment_saved, Toast.LENGTH_LONG).show(); + } - @Override - protected void onException(Bundle args, Throwable ex) { - Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); - Toast.makeText(ActivityView.this, ex.toString(), Toast.LENGTH_LONG).show(); - } - }.load(this, args); - } + @Override + protected void onException(Bundle args, Throwable ex) { + Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + Toast.makeText(ActivityView.this, ex.toString(), Toast.LENGTH_LONG).show(); + } + }.load(this, args); + } } private BillingClientStateListener billingClientStateListener = new BillingClientStateListener() { diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index be451b6194..41a6492f66 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -28,6 +28,7 @@ import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.provider.OpenableColumns; import android.text.Html; @@ -428,14 +429,15 @@ public class FragmentCompose extends FragmentEx { private void onMenuEncrypt() { Log.i(Helper.TAG, "On encrypt"); try { - if (openPgpConnection == null) - throw new IllegalArgumentException(); - if (!openPgpConnection.isBound()) - throw new IllegalArgumentException("OpenPgp not available"); + if (!PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("pro", false)) + throw new IllegalArgumentException(getString(R.string.title_pro_feature)); + + if (openPgpConnection == null || !openPgpConnection.isBound()) + throw new IllegalArgumentException(getString(R.string.title_no_openpgp)); EntityIdentity identity = (EntityIdentity) spFrom.getSelectedItem(); if (identity == null) - throw new IllegalArgumentException("No identity selected"); + throw new IllegalArgumentException(getString(R.string.title_from_missing)); Intent data = new Intent(); data.setAction(OpenPgpApi.ACTION_ENCRYPT); @@ -463,32 +465,39 @@ public class FragmentCompose extends FragmentEx { case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: { Log.i(Helper.TAG, "User interaction"); PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); - getActivity().startIntentSenderForResult( + startIntentSenderForResult( pi.getIntentSender(), ActivityCompose.REQUEST_OPENPGP, - null, 0, 0, 0); + null, 0, 0, 0, + new Bundle()); break; } case OpenPgpApi.RESULT_CODE_ERROR: { OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); - Log.e(Helper.TAG, error.getMessage()); - Toast.makeText(getContext(), error.getMessage(), Toast.LENGTH_LONG).show(); + throw new IllegalArgumentException(error.getMessage()); } } } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); - Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else + Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); } } }); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); - Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else + Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.i(Helper.TAG, "Compose onActivityResult request=" + requestCode + " result=" + resultCode + " data=" + data); if (resultCode == RESULT_OK) { if (requestCode == ActivityCompose.REQUEST_ATTACHMENT) { if (data != null) @@ -846,11 +855,16 @@ public class FragmentCompose extends FragmentEx { new SimpleTask() { @Override protected Spanned onLoad(Context context, Bundle args) throws Throwable { - return Html.fromHtml(EntityMessage.read(context, args.getLong("id"))); + String body = EntityMessage.read(context, args.getLong("id")); + if (body != null && body.startsWith("-----BEGIN PGP MESSAGE-----")) + args.putString("encrypted", body); + return Html.fromHtml(body); } @Override protected void onLoaded(Bundle args, Spanned body) { + FragmentCompose.this.encrypted = args.getString("encrypted"); + getActivity().invalidateOptionsMenu(); etBody.setText(body); etBody.setSelection(0); } diff --git a/app/src/main/java/eu/faircode/email/FragmentMessage.java b/app/src/main/java/eu/faircode/email/FragmentMessage.java index 3af846b0f5..35fa995422 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessage.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessage.java @@ -55,6 +55,7 @@ import android.widget.Toast; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.util.OpenPgpApi; @@ -353,6 +354,7 @@ public class FragmentMessage extends FragmentEx { outState.putInt("tag_cc", (int) tvCc.getTag()); outState.putInt("tag_error", (int) tvError.getTag()); } + outState.putString("decrypted", decrypted); } @Override @@ -407,6 +409,7 @@ public class FragmentMessage extends FragmentEx { rvAttachment.setTag(savedInstanceState.getInt("tag_attachment")); tvError.setTag(savedInstanceState.getInt("tag_error")); } + decrypted = savedInstanceState.getString("decrypted"); } getActivity().invalidateOptionsMenu(); @@ -424,7 +427,7 @@ public class FragmentMessage extends FragmentEx { protected Spanned onLoad(final Context context, final Bundle args) throws Throwable { final long id = args.getLong("id"); final boolean show_images = args.getBoolean("show_images"); - String body = EntityMessage.read(context, id); + String body = (decrypted == null ? EntityMessage.read(context, id) : decrypted); args.putInt("size", body.length()); return Html.fromHtml(HtmlHelper.sanitize(getContext(), body, false), new Html.ImageGetter() { @@ -755,10 +758,14 @@ public class FragmentMessage extends FragmentEx { private void onMenuDecrypt(EntityMessage message) { Log.i(Helper.TAG, "On decrypt"); try { - if (openPgpConnection == null) - throw new IllegalArgumentException(); - if (!openPgpConnection.isBound()) - throw new IllegalArgumentException("OpenPgp not available"); + if (!PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("pro", false)) + throw new IllegalArgumentException(getString(R.string.title_pro_feature)); + + if (openPgpConnection == null || !openPgpConnection.isBound()) + throw new IllegalArgumentException(getString(R.string.title_no_openpgp)); + + if (message.to == null || message.to.length == 0) + throw new IllegalArgumentException(getString(R.string.title_to_missing)); InternetAddress to = (InternetAddress) message.to[0]; @@ -787,32 +794,39 @@ public class FragmentMessage extends FragmentEx { case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: { Log.i(Helper.TAG, "User interaction"); PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); - getActivity().startIntentSenderForResult( + startIntentSenderForResult( pi.getIntentSender(), ActivityView.REQUEST_OPENPGP, - null, 0, 0, 0); + null, 0, 0, 0, + new Bundle()); break; } case OpenPgpApi.RESULT_CODE_ERROR: { OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); - Log.e(Helper.TAG, error.getMessage()); - Toast.makeText(getContext(), error.getMessage(), Toast.LENGTH_LONG).show(); + throw new IllegalArgumentException(error.getMessage()); } } } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); - Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else + Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); } } }); } catch (Throwable ex) { Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); - Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else + Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.i(Helper.TAG, "Message onActivityResult request=" + requestCode + " result=" + resultCode + " data=" + data); if (resultCode == RESULT_OK) { if (requestCode == ActivityView.REQUEST_OPENPGP && message != null) { Log.i(Helper.TAG, "User interacted"); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9a6f190d1a..7900e101b2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -160,6 +160,7 @@ Show CC/BCC Add attachment + OpenPGP not available Encrypt Decrypt @@ -179,6 +180,7 @@ Connected Closing + This is a pro feature All pro features are activated All pro features activated Invalid response