From 336c43cfde7f96937277b75674f43a645a23871f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 30 Mar 2018 22:20:44 +0200 Subject: [PATCH] Actually save key in identity select dialog, and some more design updates --- .../ui/dialog/RemoteSelectIdKeyActivity.java | 101 +++++++++- .../ui/dialog/RemoteSelectIdViewModel.java | 1 - .../RemoteSelectIdentityKeyPresenter.java | 50 ++++- .../keychain/service/ImportKeyringParcel.java | 5 + .../res/layout/api_select_identity_key.xml | 183 ++++++++++++++---- OpenKeychain/src/main/res/values/strings.xml | 3 +- 6 files changed, 286 insertions(+), 57 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdKeyActivity.java index 71ba94cec..74728f4f8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdKeyActivity.java @@ -31,6 +31,7 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable.ConstantState; import android.os.Bundle; +import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.transition.Fade; import android.support.transition.Transition; @@ -50,18 +51,26 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnDismissListener; import android.widget.TextView; +import android.widget.Toast; import com.mikepenz.materialdrawer.util.KeyboardUtil; import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.livedata.KeyInfoInteractor.KeyInfo; +import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.remote.ui.dialog.RemoteSelectIdentityKeyPresenter.RemoteSelectIdentityKeyView; +import org.sufficientlysecure.keychain.service.ImportKeyringParcel; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper.AbstractCallback; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; import org.sufficientlysecure.keychain.ui.util.ThemeChanger; import org.sufficientlysecure.keychain.ui.util.recyclerview.DividerItemDecoration; import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerItemClickListener; import org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator; +import timber.log.Timber; public class RemoteSelectIdKeyActivity extends FragmentActivity { @@ -71,6 +80,7 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { private RemoteSelectIdentityKeyPresenter presenter; + private Parcelable currentlyImportingParcel; @Override @@ -115,6 +125,7 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { private View buttonNoKeysCancel; private View buttonNoKeysExisting; private View buttonKeyListOther; + private View buttonOverflow; @NonNull @Override @@ -129,6 +140,8 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { ViewGroup view = (ViewGroup) layoutInflater.inflate(R.layout.api_select_identity_key, null, false); alert.setView(view); + buttonOverflow = view.findViewById(R.id.overflow_menu); + buttonKeyListCancel = view.findViewById(R.id.button_key_list_cancel); buttonKeyListOther = view.findViewById(R.id.button_key_list_other); @@ -182,7 +195,7 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { @NonNull private RemoteSelectIdentityKeyView createMvpView(final ViewGroup rootView, LayoutInflater layoutInflater) { - final ImageView iconClientApp = rootView.findViewById(R.id.icon_client_app); + // final ImageView iconClientApp = rootView.findViewById(R.id.icon_client_app); final KeyChoiceAdapter keyChoiceAdapter = new KeyChoiceAdapter(layoutInflater, getResources()); final TextView titleText = rootView.findViewById(R.id.text_title_select_key); final TextView addressText = rootView.findViewById(R.id.text_user_id); @@ -217,7 +230,7 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { @Override public void setTitleClientIconAndName(Drawable drawable, CharSequence name) { titleText.setText(getString(R.string.title_select_key, name)); - iconClientApp.setImageDrawable(drawable); + // iconClientApp.setImageDrawable(drawable); setSelectionIcons(drawable); } @@ -270,6 +283,11 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { layoutAnimator.setDisplayedChildId(R.id.select_key_layout_generate_ok); } + @Override + public void showLayoutGenerateSave() { + layoutAnimator.setDisplayedChildId(R.id.select_key_layout_generate_save); + } + @Override public void setKeyListData(List data) { keyChoiceAdapter.setData(data); @@ -292,10 +310,26 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { buttonKeyListCancel.setVisibility(View.INVISIBLE); keyChoiceAdapter.setActiveItem(position); } + + @Override + public void showImportInternalError() { + Toast.makeText(getContext(), R.string.error_save_key_internal, Toast.LENGTH_LONG).show(); + } + + @Override + public void launchImportOperation(ImportKeyringParcel importKeyringParcel) { + RemoteSelectIdKeyActivity activity = (RemoteSelectIdKeyActivity) getActivity(); + if (activity == null) { + return; + } + activity.launchImportOperation(importKeyringParcel); + } }; } private void setupListenersForPresenter() { + buttonOverflow.setOnClickListener(this::showContextMenu); + buttonKeyListOther.setOnClickListener(view -> presenter.onClickKeyListOther()); buttonKeyListCancel.setOnClickListener(view -> presenter.onClickKeyListCancel()); @@ -312,6 +346,23 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { keyChoiceList.addOnItemTouchListener(new RecyclerItemClickListener(getContext(), (view, position) -> presenter.onKeyItemClick(position))); } + + private void showContextMenu(View view) { + PopupMenu menu = new PopupMenu(getActivity(), view); + menu.inflate(R.menu.identity_context_menu); + // menu.setOnMenuItemClickListener(DecryptListFragment.this); + menu.show(); + } + } + + private void launchImportOperation(ImportKeyringParcel importKeyringParcel) { + if (currentlyImportingParcel != null) { + Timber.e("Starting import while already running? Inconsistent state!"); + return; + } + + currentlyImportingParcel = importKeyringParcel; + importOpHelper.cryptoOperation(); } private static class KeyChoiceAdapter extends Adapter { @@ -333,21 +384,21 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { return new KeyChoiceViewHolder(keyChoiceItemView); } - public void setActiveItem(Integer activeItem) { - if (this.activeItem != null) { - notifyItemChanged(activeItem); - } + void setActiveItem(Integer activeItem) { this.activeItem = activeItem; - if (this.activeItem != null) { - notifyItemChanged(activeItem); - } + notifyDataSetChanged(); } @Override public void onBindViewHolder(KeyChoiceViewHolder holder, int position) { KeyInfo keyInfo = data.get(position); - Drawable icon = (activeItem != null && position == activeItem) ? iconSelected : iconUnselected; + boolean hasActiveItem = activeItem != null; + boolean isActiveItem = hasActiveItem && position == activeItem; + + Drawable icon = isActiveItem ? iconSelected : iconUnselected; holder.bind(keyInfo, icon); + + holder.itemView.setVisibility(!hasActiveItem || isActiveItem ? View.VISIBLE : View.INVISIBLE); } @Override @@ -400,4 +451,34 @@ public class RemoteSelectIdKeyActivity extends FragmentActivity { } } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (importOpHelper.handleActivityResult(requestCode, resultCode, data)) { + return; + } + + super.onActivityResult(requestCode, resultCode, data); + } + + private CryptoOperationHelper importOpHelper = + new CryptoOperationHelper<>(0, this, + new AbstractCallback() { + @Override + public Parcelable createOperationInput() { + return currentlyImportingParcel; + } + + @Override + public void onCryptoOperationError(ImportKeyResult result) { + currentlyImportingParcel = null; + presenter.onImportOpError(); + } + + @Override + public void onCryptoOperationSuccess(ImportKeyResult result) { + currentlyImportingParcel = null; + presenter.onImportOpSuccess(result); + } + }, null); + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdViewModel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdViewModel.java index e2325d4ae..f5482dcae 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdViewModel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdViewModel.java @@ -12,7 +12,6 @@ public class RemoteSelectIdViewModel extends ViewModel { private KeyInfoLiveData keyInfo; private PgpKeyGenerationLiveData keyGenerationData; - public long selectedMasterKeyId; public KeyInfoLiveData getKeyInfo(Context context) { if (keyInfo == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdentityKeyPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdentityKeyPresenter.java index 716d3e354..2cb055167 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdentityKeyPresenter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteSelectIdentityKeyPresenter.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.remote.ui.dialog; +import java.io.IOException; import java.util.List; import android.arch.lifecycle.LifecycleOwner; @@ -32,9 +33,13 @@ import org.openintents.openpgp.util.OpenPgpUtils.UserId; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.livedata.KeyInfoInteractor.KeyInfo; import org.sufficientlysecure.keychain.livedata.KeyInfoInteractor.KeySelector; +import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import timber.log.Timber; @@ -44,11 +49,12 @@ class RemoteSelectIdentityKeyPresenter { private final RemoteSelectIdViewModel viewModel; - private UserId userId; - private RemoteSelectIdentityKeyView view; private List keyInfoData; - private long masterKeyId; + + private UserId userId; + private long selectedMasterKeyId; + private byte[] generatedKeyData; RemoteSelectIdentityKeyPresenter(Context context, RemoteSelectIdViewModel viewModel, LifecycleOwner lifecycleOwner) { @@ -106,11 +112,18 @@ class RemoteSelectIdentityKeyPresenter { } private void onChangeKeyGeneration(PgpEditKeyResult pgpEditKeyResult) { - viewModel.getKeyGenerationLiveData(context).setSaveKeyringParcel(null); if (pgpEditKeyResult == null) { return; } + try { + UncachedKeyRing generatedRing = pgpEditKeyResult.getRing(); + this.generatedKeyData = generatedRing.getEncoded(); + } catch (IOException e) { + throw new AssertionError("Newly generated key ring must be encodable!"); + } + + viewModel.getKeyGenerationLiveData(context).setSaveKeyringParcel(null); view.showLayoutGenerateOk(); } @@ -145,7 +158,7 @@ class RemoteSelectIdentityKeyPresenter { } void onKeyItemClick(int position) { - viewModel.selectedMasterKeyId = keyInfoData.get(position).getMasterKeyId(); + selectedMasterKeyId = keyInfoData.get(position).getMasterKeyId(); view.highlightKey(position); } @@ -162,12 +175,28 @@ class RemoteSelectIdentityKeyPresenter { } void onClickGenerateOkFinish() { - // saveKey - // view.finishAndReturn + if (generatedKeyData == null) { + return; + } + + ImportKeyringParcel importKeyringParcel = ImportKeyringParcel.createFromBytes(generatedKeyData); + generatedKeyData = null; + + view.launchImportOperation(importKeyringParcel); + view.showLayoutGenerateSave(); } void onHighlightFinished() { - view.finishAndReturn(viewModel.selectedMasterKeyId); + view.finishAndReturn(selectedMasterKeyId); + } + + void onImportOpSuccess(ImportKeyResult result) { + long importedMasterKeyId = result.getImportedMasterKeyIds()[0]; + view.finishAndReturn(importedMasterKeyId); + } + + void onImportOpError() { + view.showImportInternalError(); } interface RemoteSelectIdentityKeyView { @@ -183,9 +212,14 @@ class RemoteSelectIdentityKeyPresenter { void showLayoutImportExplanation(); void showLayoutGenerateProgress(); void showLayoutGenerateOk(); + void showLayoutGenerateSave(); void setKeyListData(List data); void highlightKey(int position); + + void launchImportOperation(ImportKeyringParcel importKeyringParcel); + + void showImportInternalError(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java index 13f31474c..582d19bb7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java @@ -50,6 +50,11 @@ public abstract class ImportKeyringParcel implements Parcelable { return new AutoValue_ImportKeyringParcel(Collections.singletonList(key), null, false); } + public static ImportKeyringParcel createFromBytes(byte[] keyData) { + ParcelableKeyRing keyRing = ParcelableKeyRing.createFromEncodedBytes(keyData); + return new AutoValue_ImportKeyringParcel(Collections.singletonList(keyRing), null, false); + } + public static ImportKeyringParcel createFromFileCacheWithSkipSave() { return new AutoValue_ImportKeyringParcel(null, null, true); } diff --git a/OpenKeychain/src/main/res/layout/api_select_identity_key.xml b/OpenKeychain/src/main/res/layout/api_select_identity_key.xml index 73d6e2fc7..141ce5c63 100644 --- a/OpenKeychain/src/main/res/layout/api_select_identity_key.xml +++ b/OpenKeychain/src/main/res/layout/api_select_identity_key.xml @@ -2,8 +2,8 @@ @@ -13,30 +13,37 @@ android:layout_height="wrap_content" android:background="?attr/colorPrimary" android:elevation="4dp" - android:gravity="center_horizontal" android:padding="16dp" + android:gravity="center_vertical" tools:targetApi="lollipop"> - + + @@ -69,8 +76,8 @@ android:layout_height="wrap_content" android:inAnimation="@anim/fade_in" android:outAnimation="@anim/fade_out" - android:measureAllChildren="true" - custom:initialView="05"> + android:measureAllChildren="false" + custom:initialView="04"> @@ -231,7 +238,7 @@ android:layout_height="24dp" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" - android:src="@drawable/ic_save_black_24dp" + android:src="@drawable/ic_help_black_24dp" android:tint="@color/md_grey_600" /> @@ -285,7 +292,7 @@ android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" - android:text="To use an existing end-to-end key, you can either:\n\n① Open Autocrypt Setup Message from another e-mail app in K-9 Mail\n\n② Manually manage keys in OpenKeychain" + android:text="To import your key, you can either:\n\n① Open Autocrypt Setup Message from another e-mail app in K-9 Mail\n\n② Manually manage keys in OpenKeychain" android:textAppearance="?android:attr/textAppearanceMedium" /> @@ -330,35 +337,71 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?listPreferredItemHeight" - android:orientation="horizontal" - android:id="@+id/select_key_layout_generate_progress" - android:layout_marginTop="36dp" + android:orientation="vertical" android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingRight="16dp" + android:id="@+id/select_key_layout_generate_progress"> - - - + android:minHeight="?listPreferredItemHeight"> + + + + + + + + + +