From 83d5aafadb58efe7098795ada2f3b69a511d23a6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 26 Jun 2018 17:19:18 +0200 Subject: [PATCH] use LiveData for signing key spinners --- .../keychain/provider/KeyRepository.java | 2 + .../AppSettingsAllowedKeysListFragment.java | 2 +- .../ui/dialog/RemoteDeduplicateActivity.java | 98 +------- .../keychain/ui/CertifyKeyFragment.java | 17 +- .../ui/EncryptModeAsymmetricFragment.java | 31 +-- .../keychain/ui/KeyListFragment.java | 49 ++-- .../keychain/ui/adapter/KeyChoiceAdapter.java | 115 ++++++++++ .../keychain/ui/keyview/GenericViewModel.java | 23 ++ .../ui/keyview/LinkedIdViewFragment.java | 61 ++++- .../keychain/ui/keyview/ViewKeyActivity.java | 1 + .../keychain/ui/widget/CertifyKeySpinner.java | 136 ----------- .../ui/widget/KeyChoiceSpinnerAdapter.java | 211 ++++++++++++++++++ .../keychain/ui/widget/KeySpinner.java | 178 +++------------ .../keychain/ui/widget/SignKeySpinner.java | 92 -------- .../main/res/layout/certify_key_fragment.xml | 2 +- .../layout/encrypt_asymmetric_fragment.xml | 4 +- .../main/res/layout/keyspinner_item_none.xml | 2 +- .../res/layout/linked_id_view_fragment.xml | 4 +- 18 files changed, 492 insertions(+), 536 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyChoiceAdapter.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/GenericViewModel.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyChoiceSpinnerAdapter.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java index 297741f9b..5eb4f1ad8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java @@ -25,6 +25,7 @@ import java.util.List; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; +import android.support.annotation.WorkerThread; import com.squareup.sqldelight.SqlDelightQuery; import org.bouncycastle.bcpg.ArmoredOutputStream; @@ -43,6 +44,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import timber.log.Timber; +@WorkerThread public class KeyRepository extends AbstractDao { final ContentResolver contentResolver; final LocalPublicKeyStorage mLocalPublicKeyStorage; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java index ee9dbbf6c..b08d701ca 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java @@ -127,7 +127,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i Vector userIds = new Vector<>(); for (int i = 0; i < getListView().getCount(); ++i) { if (getListView().isItemChecked(i)) { - userIds.add(mAdapter.getUserId(i)); + userIds.add(spinnerAdapter.getUserId(i)); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteDeduplicateActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteDeduplicateActivity.java index 1b62788c8..1d932b7ef 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteDeduplicateActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteDeduplicateActivity.java @@ -29,23 +29,16 @@ import android.arch.lifecycle.ViewModelProviders; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.res.Resources; import android.graphics.drawable.Drawable; -import android.graphics.drawable.Drawable.ConstantState; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; -import android.support.v4.content.res.ResourcesCompat; -import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.RecyclerView.Adapter; -import android.text.format.DateUtils; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; @@ -57,6 +50,7 @@ import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.remote.ui.RemoteSecurityTokenOperationActivity; import org.sufficientlysecure.keychain.remote.ui.dialog.RemoteDeduplicatePresenter.RemoteDeduplicateView; +import org.sufficientlysecure.keychain.ui.adapter.KeyChoiceAdapter; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; import org.sufficientlysecure.keychain.ui.util.ThemeChanger; import org.sufficientlysecure.keychain.ui.util.recyclerview.DividerItemDecoration; @@ -263,94 +257,4 @@ public class RemoteDeduplicateActivity extends FragmentActivity { } } - private static class KeyChoiceAdapter extends Adapter { - private final LayoutInflater layoutInflater; - private final Resources resources; - private List data; - private Drawable iconUnselected; - private Drawable iconSelected; - private Integer activeItem; - - KeyChoiceAdapter(LayoutInflater layoutInflater, Resources resources) { - this.layoutInflater = layoutInflater; - this.resources = resources; - } - - @NonNull - @Override - public KeyChoiceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View keyChoiceItemView = layoutInflater.inflate(R.layout.duplicate_key_item, parent, false); - return new KeyChoiceViewHolder(keyChoiceItemView); - } - - @Override - public void onBindViewHolder(@NonNull KeyChoiceViewHolder holder, int position) { - UnifiedKeyInfo keyInfo = data.get(position); - Drawable icon = (activeItem != null && position == activeItem) ? iconSelected : iconUnselected; - holder.bind(keyInfo, icon); - } - - @Override - public int getItemCount() { - return data != null ? data.size() : 0; - } - - public void setData(List data) { - this.data = data; - notifyDataSetChanged(); - } - - void setSelectionDrawable(Drawable drawable) { - ConstantState constantState = drawable.getConstantState(); - if (constantState == null) { - return; - } - - iconSelected = constantState.newDrawable(resources); - - iconUnselected = constantState.newDrawable(resources); - DrawableCompat.setTint(iconUnselected.mutate(), ResourcesCompat.getColor(resources, R.color.md_grey_300, null)); - - notifyDataSetChanged(); - } - - void setActiveItem(Integer newActiveItem) { - Integer prevActiveItem = this.activeItem; - this.activeItem = newActiveItem; - - if (prevActiveItem != null) { - notifyItemChanged(prevActiveItem); - } - if (newActiveItem != null) { - notifyItemChanged(newActiveItem); - } - } - } - - private static class KeyChoiceViewHolder extends RecyclerView.ViewHolder { - private final TextView vName; - private final TextView vCreation; - private final ImageView vIcon; - - KeyChoiceViewHolder(View itemView) { - super(itemView); - - vName = itemView.findViewById(R.id.key_list_item_name); - vCreation = itemView.findViewById(R.id.key_list_item_creation); - vIcon = itemView.findViewById(R.id.key_list_item_icon); - } - - void bind(UnifiedKeyInfo keyInfo, Drawable selectionIcon) { - vName.setText(keyInfo.name()); - - Context context = vCreation.getContext(); - String dateTime = DateUtils.formatDateTime(context, keyInfo.creation(), - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | - DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_MONTH); - vCreation.setText(context.getString(R.string.label_key_created, dateTime)); - - vIcon.setImageDrawable(selectionIcon); - } - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index 24228b655..059cc683b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -20,8 +20,11 @@ package org.sufficientlysecure.keychain.ui; import java.util.ArrayList; import java.util.Date; +import java.util.List; import android.app.Activity; +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.ViewModelProviders; import android.content.Intent; import android.graphics.PorterDuff; import android.os.Bundle; @@ -42,9 +45,10 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment; +import org.sufficientlysecure.keychain.ui.keyview.GenericViewModel; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; +import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.Preferences; @@ -53,7 +57,7 @@ public class CertifyKeyFragment private CheckBox mUploadKeyCheckbox; - private CertifyKeySpinner mCertifyKeySpinner; + private KeySpinner mCertifyKeySpinner; private MultiUserIdsFragment mMultiUserIdsFragment; @@ -61,6 +65,13 @@ public class CertifyKeyFragment public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + GenericViewModel viewModel = ViewModelProviders.of(this).get(GenericViewModel.class); + LiveData> liveData = viewModel.getGenericLiveData(requireContext(), () -> { + KeyRepository keyRepository = KeyRepository.create(requireContext()); + return keyRepository.getAllUnifiedKeyInfoWithSecret(); + }); + liveData.observe(this, mCertifyKeySpinner::setData); + if (savedInstanceState == null) { // preselect certify key id if given long certifyKeyId = getActivity().getIntent() @@ -90,6 +101,8 @@ public class CertifyKeyFragment mMultiUserIdsFragment = (MultiUserIdsFragment) getChildFragmentManager().findFragmentById(R.id.multi_user_ids_fragment); + mCertifyKeySpinner.setShowNone(R.string.choice_select_cert); + // make certify image gray, like action icons ImageView vActionCertifyImage = view.findViewById(R.id.certify_key_action_certify_image); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java index 0f92481ab..6e4c15675 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java @@ -22,7 +22,10 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.ViewModelProviders; import android.os.Bundle; +import android.support.annotation.NonNull; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -36,12 +39,12 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem; +import org.sufficientlysecure.keychain.ui.keyview.GenericViewModel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView; import org.sufficientlysecure.keychain.ui.widget.KeySpinner; -import org.sufficientlysecure.keychain.ui.widget.KeySpinner.OnKeyChangedListener; import org.sufficientlysecure.keychain.util.Passphrase; import timber.log.Timber; @@ -57,9 +60,6 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment { public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids"; - /** - * Creates new instance of this fragment - */ public static EncryptModeAsymmetricFragment newInstance(long signatureKey, long[] encryptionKeyIds) { EncryptModeAsymmetricFragment frag = new EncryptModeAsymmetricFragment(); @@ -75,23 +75,21 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment { * Inflate the layout for this fragment */ @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false); - mSignKeySpinner = view.findViewById(R.id.sign); + mSignKeySpinner = view.findViewById(R.id.sign_key_spinner); mEncryptKeyView = view.findViewById(R.id.recipient_list); mEncryptKeyView.setThreshold(1); // Start working from first character final ViewAnimator vSignatureIcon = view.findViewById(R.id.result_signature_icon); - mSignKeySpinner.setOnKeyChangedListener(new OnKeyChangedListener() { - @Override - public void onKeyChanged(long masterKeyId) { - int child = masterKeyId != Constants.key.none ? 1 : 0; - if (vSignatureIcon.getDisplayedChild() != child) { - vSignatureIcon.setDisplayedChild(child); - } + mSignKeySpinner.setOnKeyChangedListener(masterKeyId -> { + int child = masterKeyId != Constants.key.none ? 1 : 0; + if (vSignatureIcon.getDisplayedChild() != child) { + vSignatureIcon.setDisplayedChild(child); } }); + mSignKeySpinner.setShowNone(R.string.cert_none); final ViewAnimator vEncryptionIcon = view.findViewById(R.id.result_encryption_icon); mEncryptKeyView.setTokenListener(new TokenListener() { @@ -117,7 +115,12 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mKeyRepository = KeyRepository.create(getContext()); + mKeyRepository = KeyRepository.create(requireContext()); + + GenericViewModel viewModel = ViewModelProviders.of(this).get(GenericViewModel.class); + LiveData> liveData = viewModel.getGenericLiveData(requireContext(), + mKeyRepository::getAllUnifiedKeyInfoWithSecret); + liveData.observe(this, mSignKeySpinner::setData); // preselect keys given, from state or arguments if (savedInstanceState == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 635919a1c..24b59291f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -24,15 +24,12 @@ import java.util.List; import android.animation.ObjectAnimator; import android.app.Activity; import android.arch.lifecycle.LiveData; -import android.arch.lifecycle.ViewModel; import android.arch.lifecycle.ViewModelProviders; -import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; -import android.support.annotation.NonNull; +import android.support.annotation.WorkerThread; import android.support.v4.app.FragmentActivity; -import android.support.v4.os.CancellationSignal; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -58,7 +55,6 @@ import eu.davidea.flexibleadapter.SelectableAdapter.Mode; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.operations.KeySyncOperation; import org.sufficientlysecure.keychain.keysync.KeyserverSyncManager; import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.operations.KeySyncParcel; @@ -78,8 +74,8 @@ import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyItem.FlexibleSectio import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyItemFactory; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.RecyclerFragment; +import org.sufficientlysecure.keychain.ui.keyview.GenericViewModel; import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity; -import org.sufficientlysecure.keychain.ui.keyview.loader.AsyncTaskLiveData; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FabContainer; @@ -101,6 +97,9 @@ public class KeyListFragment extends RecyclerFragment> liveData = viewModel.getGenericLiveData(requireContext(), this::loadFlexibleKeyItems); + liveData.observe(this, this::onLoadKeyItems); } - public static class KeyListViewModel extends ViewModel { - LiveData> liveData; - - LiveData> getLiveData(Context context) { - if (liveData == null) { - liveData = new KeyListLiveData(context); - } - return liveData; - } - } - - public static class KeyListLiveData extends AsyncTaskLiveData> { - private final KeyRepository keyRepository; - private FlexibleKeyItemFactory flexibleKeyItemFactory; - - KeyListLiveData(@NonNull Context context) { - super(context, KeyRings.CONTENT_URI); - keyRepository = KeyRepository.create(context.getApplicationContext()); - flexibleKeyItemFactory = new FlexibleKeyItemFactory(context.getResources()); - } - - @Override - protected List asyncLoadData() { - List unifiedKeyInfo = keyRepository.getAllUnifiedKeyInfo(); - return flexibleKeyItemFactory.mapUnifiedKeyInfoToFlexibleKeyItems(unifiedKeyInfo); - } + @WorkerThread + private List loadFlexibleKeyItems() { + List unifiedKeyInfo = keyRepository.getAllUnifiedKeyInfo(); + return flexibleKeyItemFactory.mapUnifiedKeyInfoToFlexibleKeyItems(unifiedKeyInfo); } private void onLoadKeyItems(List flexibleKeyItems) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyChoiceAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyChoiceAdapter.java new file mode 100644 index 000000000..1448e9f31 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyChoiceAdapter.java @@ -0,0 +1,115 @@ +package org.sufficientlysecure.keychain.ui.adapter; + + +import java.util.List; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Drawable.ConstantState; +import android.support.annotation.NonNull; +import android.support.v4.content.res.ResourcesCompat; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.Adapter; +import android.text.format.DateUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; +import org.sufficientlysecure.keychain.ui.adapter.KeyChoiceAdapter.KeyChoiceViewHolder; + + +public class KeyChoiceAdapter extends Adapter { + private final LayoutInflater layoutInflater; + private final Resources resources; + private List data; + private Drawable iconUnselected; + private Drawable iconSelected; + private Integer activeItem; + + public KeyChoiceAdapter(LayoutInflater layoutInflater, Resources resources) { + this.layoutInflater = layoutInflater; + this.resources = resources; + } + + @NonNull + @Override + public KeyChoiceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View keyChoiceItemView = layoutInflater.inflate(R.layout.duplicate_key_item, parent, false); + return new KeyChoiceViewHolder(keyChoiceItemView); + } + + @Override + public void onBindViewHolder(@NonNull KeyChoiceViewHolder holder, int position) { + UnifiedKeyInfo keyInfo = data.get(position); + Drawable icon = (activeItem != null && position == activeItem) ? iconSelected : iconUnselected; + holder.bind(keyInfo, icon); + } + + @Override + public int getItemCount() { + return data != null ? data.size() : 0; + } + + public void setData(List data) { + this.data = data; + notifyDataSetChanged(); + } + + public void setSelectionDrawable(Drawable drawable) { + ConstantState constantState = drawable.getConstantState(); + if (constantState == null) { + return; + } + + iconSelected = constantState.newDrawable(resources); + + iconUnselected = constantState.newDrawable(resources); + DrawableCompat.setTint(iconUnselected.mutate(), ResourcesCompat.getColor(resources, R.color.md_grey_300, null)); + + notifyDataSetChanged(); + } + + public void setActiveItem(Integer newActiveItem) { + Integer prevActiveItem = this.activeItem; + this.activeItem = newActiveItem; + + if (prevActiveItem != null) { + notifyItemChanged(prevActiveItem); + } + if (newActiveItem != null) { + notifyItemChanged(newActiveItem); + } + } + + public static class KeyChoiceViewHolder extends RecyclerView.ViewHolder { + private final TextView vName; + private final TextView vCreation; + private final ImageView vIcon; + + KeyChoiceViewHolder(View itemView) { + super(itemView); + + vName = itemView.findViewById(R.id.key_list_item_name); + vCreation = itemView.findViewById(R.id.key_list_item_creation); + vIcon = itemView.findViewById(R.id.key_list_item_icon); + } + + void bind(UnifiedKeyInfo keyInfo, Drawable selectionIcon) { + vName.setText(keyInfo.name()); + + Context context = vCreation.getContext(); + String dateTime = DateUtils.formatDateTime(context, keyInfo.creation(), + DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | + DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_MONTH); + vCreation.setText(context.getString(R.string.label_key_created, dateTime)); + + vIcon.setImageDrawable(selectionIcon); + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/GenericViewModel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/GenericViewModel.java new file mode 100644 index 000000000..ab4054297 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/GenericViewModel.java @@ -0,0 +1,23 @@ +package org.sufficientlysecure.keychain.ui.keyview; + + +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.ViewModel; +import android.content.Context; + +import org.sufficientlysecure.keychain.livedata.GenericLiveData; +import org.sufficientlysecure.keychain.livedata.GenericLiveData.GenericDataLoader; + + +/** A simple generic ViewModel that can be used if exactly one field of data needs to be stored. */ +public class GenericViewModel extends ViewModel { + private LiveData genericLiveData; + + public LiveData getGenericLiveData(Context context, GenericDataLoader func) { + if (genericLiveData == null) { + genericLiveData = new GenericLiveData<>(context, null, func); + } + // noinspection unchecked + return genericLiveData; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java index 0e59421d0..664946a9a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java @@ -19,8 +19,11 @@ package org.sufficientlysecure.keychain.ui.keyview; import java.util.Collections; +import java.util.List; import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.ViewModel; +import android.arch.lifecycle.ViewModelProviders; import android.content.Context; import android.content.Intent; import android.graphics.PorterDuff; @@ -52,6 +55,7 @@ import org.sufficientlysecure.keychain.linked.UriAttribute; import org.sufficientlysecure.keychain.livedata.CertificationDao; import org.sufficientlysecure.keychain.livedata.GenericLiveData; import org.sufficientlysecure.keychain.model.Certification.CertDetails; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.provider.KeyRepository; @@ -69,7 +73,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker; import org.sufficientlysecure.keychain.ui.widget.CertListWidget; -import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; +import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import timber.log.Timber; @@ -118,11 +122,15 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements OnB isSecret = args.getBoolean(ARG_IS_SECRET); masterKeyId = args.getLong(ARG_MASTER_KEY_ID); + } - IdentityDao identityDao = IdentityDao.getInstance(getContext()); - GenericLiveData uriAttributeLiveData = - new GenericLiveData<>(getContext(), null, () -> identityDao.getLinkedIdInfo(masterKeyId, lidRank)); - uriAttributeLiveData.observe(this, this::onLinkedIdInfoLoaded); + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + LinkedIdViewModel viewModel = ViewModelProviders.of(this).get(LinkedIdViewModel.class); + viewModel.getLinkedIdInfo(requireContext(), masterKeyId, lidRank).observe(this, this::onLinkedIdInfoLoaded); + viewModel.getCertifyingKeys(requireContext()).observe(this, viewHolder.vKeySpinner::setData); } private void onLinkedIdInfoLoaded(LinkedIdInfo linkedIdInfo) { @@ -191,7 +199,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements OnB private ViewAnimator vButtonSwitcher; private CertListWidget vLinkedCerts; - private CertifyKeySpinner vKeySpinner; + private KeySpinner vKeySpinner; private final View vButtonVerify; private final View vButtonRetry; private final View vButtonConfirm; @@ -236,6 +244,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements OnB if (!isSecret) { showButton(2); if (!vKeySpinner.isSingleEntry()) { + vKeySpinner.setShowNone(R.string.choice_select_cert); vKeySpinnerContainer.setVisibility(View.VISIBLE); } } else { @@ -351,10 +360,8 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements OnB viewHolder.vButtonRetry.setOnClickListener(v -> verifyResource()); viewHolder.vButtonConfirm.setOnClickListener(v -> initiateCertifying()); - CertificationDao certificationDao = CertificationDao.getInstance(context); - LiveData certDetailsLiveData = new GenericLiveData<>( - context, null, () -> certificationDao.getVerifyingCertDetails(masterKeyId, lidRank)); - certDetailsLiveData.observe(this, this::onLoadCertDetails); + LinkedIdViewModel viewModel = ViewModelProviders.of(this).get(LinkedIdViewModel.class); + viewModel.getCertDetails(context, masterKeyId, lidRank).observe(this, this::onLoadCertDetails); return root; } @@ -483,4 +490,38 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements OnB return true; } + public static class LinkedIdViewModel extends ViewModel { + LiveData> certifyingKeysLiveData; + LiveData certDetailsLiveData; + LiveData linkedIfInfoLiveData; + + LiveData> getCertifyingKeys(Context context) { + if (certifyingKeysLiveData == null) { + certifyingKeysLiveData = new GenericLiveData<>(context, null, () -> { + KeyRepository keyRepository = KeyRepository.create(context); + return keyRepository.getAllUnifiedKeyInfoWithSecret(); + }); + } + return certifyingKeysLiveData; + } + + LiveData getCertDetails(Context context, long masterKeyId, int lidRank) { + if (certDetailsLiveData == null) { + CertificationDao certificationDao = CertificationDao.getInstance(context); + certDetailsLiveData = new GenericLiveData<>(context, null, + () -> certificationDao.getVerifyingCertDetails(masterKeyId, lidRank)); + } + return certDetailsLiveData; + } + + public LiveData getLinkedIdInfo(Context context, long masterKeyId, int lidRank) { + if (linkedIfInfoLiveData == null) { + IdentityDao identityDao = IdentityDao.getInstance(context); + linkedIfInfoLiveData = new GenericLiveData<>(context, null, + () -> identityDao.getLinkedIdInfo(masterKeyId, lidRank)); + } + return linkedIfInfoLiveData; + } + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java index 7772202ca..dd6cdc865 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java @@ -268,6 +268,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements UnifiedKeyInfoViewModel viewModel = ViewModelProviders.of(this).get(UnifiedKeyInfoViewModel.class); viewModel.setMasterKeyId(getIntent().getLongExtra(EXTRA_MASTER_KEY_ID, 0L)); + viewModel.getUnifiedKeyInfoLiveData(getApplicationContext()).observe(this, this::onLoadUnifiedKeyInfo); if (savedInstanceState == null && intent.hasExtra(EXTRA_DISPLAY_RESULT)) { OperationResult result = intent.getParcelableExtra(EXTRA_DISPLAY_RESULT); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java deleted file mode 100644 index 3284448a0..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * 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.widget; - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.annotation.StringRes; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.util.AttributeSet; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainDatabase; -import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; - -public class CertifyKeySpinner extends KeySpinner { - private long mHiddenMasterKeyId = Constants.key.none; - private boolean mIsSingle; - - public CertifyKeySpinner(Context context) { - super(context); - } - - public CertifyKeySpinner(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public CertifyKeySpinner(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public void setHiddenMasterKeyId(long hiddenMasterKeyId) { - this.mHiddenMasterKeyId = hiddenMasterKeyId; - reload(); - } - - @Override - public Loader onCreateLoader(int loaderId, Bundle data) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); - - String[] projection = KeyAdapter.getProjectionWith(new String[] { - KeychainContract.KeyRings.HAS_CERTIFY_SECRET, - }); - - String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " - + KeychainDatabase.Tables.KEYS + "." + KeychainContract.KeyRings.MASTER_KEY_ID - + " != " + mHiddenMasterKeyId; - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getContext(), baseUri, projection, where, null, null); - } - - private int mIndexHasCertify; - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - super.onLoadFinished(loader, data); - - if (loader.getId() == LOADER_ID) { - mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY_SECRET); - - // If: - // - no key has been pre-selected (e.g. by SageSlinger) - // - there are actually keys (not just "none" entry) - // Then: - // - select key that is capable of certifying, but only if there is only one key capable of it - if (mPreSelectedKeyId == Constants.key.none && mAdapter.getCount() > 1) { - // preselect if key can certify - int selection = -1; - while (data.moveToNext()) { - if (!data.isNull(mIndexHasCertify)) { - if (selection == -1) { - selection = data.getPosition() + 1; - mIsSingle = true; - } else { - // if selection is already set, we have more than one certify key! - // get back to "none"! - mIsSingle = false; - selection = 0; - } - } - } - setSelection(selection); - } - } - } - - public boolean isSingleEntry() { - return mIsSingle && getSelectedItemPosition() != 0; - } - - @Override - boolean isItemEnabled(Cursor cursor) { - if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) { - return false; - } - if (cursor.getInt(KeyAdapter.INDEX_IS_EXPIRED) != 0) { - return false; - } - if (cursor.isNull(mIndexHasCertify)) { - return false; - } - - // valid key - return true; - } - - @Override - public @StringRes int getNoneString() { - return R.string.choice_select_cert; - } - - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyChoiceSpinnerAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyChoiceSpinnerAdapter.java new file mode 100644 index 000000000..a27afda34 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyChoiceSpinnerAdapter.java @@ -0,0 +1,211 @@ +package org.sufficientlysecure.keychain.ui.widget; + + +import java.util.List; + +import android.content.Context; +import android.support.annotation.StringRes; +import android.text.format.DateUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; +import org.sufficientlysecure.keychain.ui.util.FormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; + + +class KeyChoiceSpinnerAdapter extends BaseAdapter { + private Integer noneItemString; + private List data; + private final LayoutInflater layoutInflater; + + KeyChoiceSpinnerAdapter(Context context) { + super(); + + layoutInflater = LayoutInflater.from(context); + } + + public void setData(List data) { + this.data = data; + notifyDataSetChanged(); + } + + public void setNoneItemString(@StringRes Integer noneItemString) { + this.noneItemString = noneItemString; + notifyDataSetChanged(); + } + + public boolean hasNoneItem() { + return noneItemString != null; + } + + @Override + public int getCount() { + return (data != null ? data.size() : 0) + (noneItemString != null ? 1 : 0); + } + + public boolean isSingleEntry() { + return data != null && data.size() == 1; + } + + @Override + public UnifiedKeyInfo getItem(int position) { + if (noneItemString != null) { + if (position == 0) { + return null; + } + position -= 1; + } + return data.get(position); + } + + @Override + public long getItemId(int position) { + if (noneItemString != null) { + if (position == 0) { + return 0; + } + position -= 1; + } + return data != null ? data.get(position).master_key_id() : 0; + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public int getItemViewType(int position) { + if (noneItemString != null && position == 0) { + return 1; + } else { + return super.getItemViewType(position); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (noneItemString != null) { + if (position == 0) { + if (convertView != null && convertView.getTag() == null) { + return convertView; + } else { + View view = layoutInflater.inflate(R.layout.keyspinner_item_none, parent, false); + view.findViewById(R.id.keyspinner_key_name).setText(noneItemString); + return view; + } + } + } + + View view; + KeyChoiceViewHolder viewHolder; + if (convertView == null || !(convertView.getTag() instanceof KeyChoiceViewHolder)) { + view = layoutInflater.inflate(R.layout.key_list_item, parent, false); + viewHolder = new KeyChoiceViewHolder(view); + view.setTag(viewHolder); + } else { + view = convertView; + viewHolder = ((KeyChoiceViewHolder) view.getTag()); + } + + UnifiedKeyInfo keyInfo = getItem(position); + viewHolder.bind(view.getContext(), keyInfo, isEnabled(position)); + + return view; + } + + public static class KeyChoiceViewHolder { + private View mView; + private TextView mMainUserId; + private TextView mMainUserIdRest; + private TextView mCreationDate; + private ImageView mStatus; + + KeyChoiceViewHolder(View view) { + mView = view; + mMainUserId = view.findViewById(R.id.key_list_item_name); + mMainUserIdRest = view.findViewById(R.id.key_list_item_email); + mStatus = view.findViewById(R.id.key_list_item_status_icon); + mCreationDate = view.findViewById(R.id.key_list_item_creation); + } + + public void bind(Context context, UnifiedKeyInfo keyInfo, boolean enabled) { + { // set name and stuff, common to both key types + if (keyInfo.name() != null) { + mMainUserId.setText(keyInfo.name()); + } else { + mMainUserId.setText(R.string.user_id_no_name); + } + if (keyInfo.email() != null) { + mMainUserIdRest.setText(keyInfo.email()); + mMainUserIdRest.setVisibility(View.VISIBLE); + } else { + mMainUserIdRest.setVisibility(View.GONE); + } + } + + // sort of a hack: if this item isn't enabled, we make it clickable + // to intercept its click events. either way, no listener! + mView.setClickable(!enabled); + + { // set edit button and status, specific by key type + + int textColor; + + // Note: order is important! + if (keyInfo.is_revoked()) { + KeyFormattingUtils + .setStatusImage(context, mStatus, null, State.REVOKED, R.color.key_flag_gray); + mStatus.setVisibility(View.VISIBLE); + textColor = context.getResources().getColor(R.color.key_flag_gray); + } else if (keyInfo.is_expired()) { + KeyFormattingUtils.setStatusImage(context, mStatus, null, State.EXPIRED, R.color.key_flag_gray); + mStatus.setVisibility(View.VISIBLE); + textColor = context.getResources().getColor(R.color.key_flag_gray); + } else if (!keyInfo.is_secure()) { + KeyFormattingUtils.setStatusImage(context, mStatus, null, State.INSECURE, R.color.key_flag_gray); + mStatus.setVisibility(View.VISIBLE); + textColor = context.getResources().getColor(R.color.key_flag_gray); + } else if (keyInfo.has_any_secret()) { + mStatus.setVisibility(View.GONE); + textColor = FormattingUtils.getColorFromAttr(context, R.attr.colorText); + } else { + // this is a public key - show if it's verified + if (keyInfo.is_verified()) { + KeyFormattingUtils.setStatusImage(context, mStatus, State.VERIFIED); + mStatus.setVisibility(View.VISIBLE); + } else { + KeyFormattingUtils.setStatusImage(context, mStatus, State.UNVERIFIED); + mStatus.setVisibility(View.VISIBLE); + } + textColor = FormattingUtils.getColorFromAttr(context, R.attr.colorText); + } + + mMainUserId.setTextColor(textColor); + mMainUserIdRest.setTextColor(textColor); + + if (keyInfo.has_duplicate()) { + String dateTime = DateUtils.formatDateTime(context, + keyInfo.creation() * 1000, + DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_SHOW_TIME + | DateUtils.FORMAT_SHOW_YEAR + | DateUtils.FORMAT_ABBREV_MONTH); + mCreationDate.setText(context.getString(R.string.label_key_created, + dateTime)); + mCreationDate.setTextColor(textColor); + mCreationDate.setVisibility(View.VISIBLE); + } else { + mCreationDate.setVisibility(View.GONE); + } + } + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index d57f78d19..27be55b8a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -18,39 +18,22 @@ package org.sufficientlysecure.keychain.ui.widget; +import java.util.List; + import android.content.Context; -import android.database.Cursor; import android.os.Bundle; import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.StringRes; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; import android.support.v7.widget.AppCompatSpinner; import android.util.AttributeSet; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.SpinnerAdapter; -import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; -import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem; - - -/** - * Use AppCompatSpinner from AppCompat lib instead of Spinner. Fixes white dropdown icon. - * Related: http://stackoverflow.com/a/27713090 - */ -public abstract class KeySpinner extends AppCompatSpinner implements - LoaderManager.LoaderCallbacks { +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; +public class KeySpinner extends AppCompatSpinner { public static final String ARG_SUPER_STATE = "super_state"; public static final String ARG_KEY_ID = "key_id"; @@ -58,13 +41,10 @@ public abstract class KeySpinner extends AppCompatSpinner implements void onKeyChanged(long masterKeyId); } - protected long mPreSelectedKeyId = Constants.key.none; - protected SelectKeyAdapter mAdapter = new SelectKeyAdapter(); + protected Long preSelectedKeyId; + protected KeyChoiceSpinnerAdapter spinnerAdapter; protected OnKeyChangedListener mListener; - // this shall note collide with other loaders inside the activity - protected int LOADER_ID = 2343; - public KeySpinner(Context context) { super(context); initView(); @@ -81,7 +61,9 @@ public abstract class KeySpinner extends AppCompatSpinner implements } private void initView() { - setAdapter(mAdapter); + spinnerAdapter = new KeyChoiceSpinnerAdapter(getContext()); + + setAdapter(spinnerAdapter); super.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { @@ -100,6 +82,10 @@ public abstract class KeySpinner extends AppCompatSpinner implements }); } + public void setShowNone(@StringRes Integer noneStringRes) { + spinnerAdapter.setNoneItemString(noneStringRes); + } + @Override public void setOnItemSelectedListener(OnItemSelectedListener listener) { throw new UnsupportedOperationException(); @@ -109,32 +95,28 @@ public abstract class KeySpinner extends AppCompatSpinner implements mListener = listener; } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - reload(); + public void setData(List keyInfos) { + spinnerAdapter.setData(keyInfos); + maybeSelectPreselection(keyInfos); } - public void reload() { - if (getContext() instanceof FragmentActivity) { - ((FragmentActivity) getContext()).getSupportLoaderManager().restartLoader(LOADER_ID, null, this); - } else { - // ignore, this happens during preview! we use fragmentactivities everywhere either way + private void maybeSelectPreselection(List keyInfos) { + if (preSelectedKeyId == null) { + return; + } + for (UnifiedKeyInfo keyInfo : keyInfos) { + if (keyInfo.master_key_id() == preSelectedKeyId) { + int position = keyInfos.indexOf(keyInfo); + if (spinnerAdapter.hasNoneItem()) { + position += 1; + } + setSelection(position); + } } } - @Override - public void onLoadFinished(Loader loader, Cursor data) { - if (loader.getId() == LOADER_ID) { - mAdapter.swapCursor(data); - } - } - - @Override - public void onLoaderReset(Loader loader) { - if (loader.getId() == LOADER_ID) { - mAdapter.swapCursor(null); - } + public boolean isSingleEntry() { + return spinnerAdapter.isSingleEntry(); } public long getSelectedKeyId() { @@ -143,108 +125,21 @@ public abstract class KeySpinner extends AppCompatSpinner implements } public long getSelectedKeyId(Object item) { - if (item instanceof KeyItem) { - return ((KeyItem) item).mKeyId; + if (item instanceof UnifiedKeyInfo) { + return ((UnifiedKeyInfo) item).master_key_id(); } return Constants.key.none; } public void setPreSelectedKeyId(long selectedKeyId) { - mPreSelectedKeyId = selectedKeyId; - } - - protected class SelectKeyAdapter extends BaseAdapter implements SpinnerAdapter { - private KeyAdapter inner; - private int mIndexMasterKeyId; - - public SelectKeyAdapter() { - inner = new KeyAdapter(getContext(), null, 0) { - - @Override - public boolean isEnabled(Cursor cursor) { - return KeySpinner.this.isItemEnabled(cursor); - } - - }; - } - - public Cursor swapCursor(Cursor newCursor) { - if (newCursor == null) return inner.swapCursor(null); - - mIndexMasterKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID); - - Cursor oldCursor = inner.swapCursor(newCursor); - - // pre-select key if mPreSelectedKeyId is given - if (mPreSelectedKeyId != Constants.key.none && newCursor.moveToFirst()) { - do { - if (newCursor.getLong(mIndexMasterKeyId) == mPreSelectedKeyId) { - setSelection(newCursor.getPosition() +1); - } - } while (newCursor.moveToNext()); - } - return oldCursor; - } - - @Override - public int getCount() { - return inner.getCount() +1; - } - - @Override - public KeyItem getItem(int position) { - if (position == 0) { - return null; - } - return inner.getItem(position -1); - } - - @Override - public long getItemId(int position) { - if (position == 0) { - return Constants.key.none; - } - return inner.getItemId(position -1); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - // Unfortunately, SpinnerAdapter does not support multiple view - // types. For this reason, we throw away convertViews of a bad - // type. This is sort of a hack, but since the number of elements - // we deal with in KeySpinners is usually very small (number of - // secret keys), this is the easiest solution. (I'm sorry.) - if (convertView != null) { - // This assumes that the inner view has non-null tags on its views! - boolean isWrongType = (convertView.getTag() == null) != (position == 0); - if (isWrongType) { - convertView = null; - } - } - - if (position > 0) { - return inner.getView(position -1, convertView, parent); - } - - View view = convertView != null ? convertView : - LayoutInflater.from(getContext()).inflate( - R.layout.keyspinner_item_none, parent, false); - ((TextView) view.findViewById(R.id.keyspinner_key_name)).setText(getNoneString()); - return view; - } - - } - - boolean isItemEnabled(Cursor cursor) { - return true; + preSelectedKeyId = selectedKeyId; } @Override public void onRestoreInstanceState(Parcelable state) { Bundle bundle = (Bundle) state; - mPreSelectedKeyId = bundle.getLong(ARG_KEY_ID); + preSelectedKeyId = bundle.getLong(ARG_KEY_ID); // restore super state super.onRestoreInstanceState(bundle.getParcelable(ARG_SUPER_STATE)); @@ -262,9 +157,4 @@ public abstract class KeySpinner extends AppCompatSpinner implements bundle.putLong(ARG_KEY_ID, getSelectedKeyId()); return bundle; } - - public @StringRes int getNoneString() { - return R.string.cert_none; - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java deleted file mode 100644 index 55b3537be..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * 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.widget; - - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.util.AttributeSet; - -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; - -public class SignKeySpinner extends KeySpinner { - public SignKeySpinner(Context context) { - super(context); - } - - public SignKeySpinner(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public SignKeySpinner(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public Loader onCreateLoader(int loaderId, Bundle data) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); - - String[] projection = KeyAdapter.getProjectionWith(new String[] { - KeychainContract.KeyRings.HAS_SIGN_SECRET, - }); - - String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1"; - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getContext(), baseUri, projection, where, null, null); - } - - private int mIndexHasSign; - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - super.onLoadFinished(loader, data); - - if (loader.getId() == LOADER_ID) { - mIndexHasSign = data.getColumnIndex(KeychainContract.KeyRings.HAS_SIGN_SECRET); - } - } - - @Override - boolean isItemEnabled(Cursor cursor) { - if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) { - return false; - } - if (cursor.getInt(KeyAdapter.INDEX_IS_EXPIRED) != 0) { - return false; - } - if (cursor.getInt(KeyAdapter.INDEX_IS_SECURE) == 0) { - return false; - } - if (cursor.isNull(mIndexHasSign)) { - return false; - } - - // valid key - return true; - } - -} diff --git a/OpenKeychain/src/main/res/layout/certify_key_fragment.xml b/OpenKeychain/src/main/res/layout/certify_key_fragment.xml index d8527e408..aa7875ae9 100644 --- a/OpenKeychain/src/main/res/layout/certify_key_fragment.xml +++ b/OpenKeychain/src/main/res/layout/certify_key_fragment.xml @@ -49,7 +49,7 @@ android:paddingRight="8dp" android:gravity="center_vertical" /> - - diff --git a/OpenKeychain/src/main/res/layout/linked_id_view_fragment.xml b/OpenKeychain/src/main/res/layout/linked_id_view_fragment.xml index 14db368bf..c121cf295 100644 --- a/OpenKeychain/src/main/res/layout/linked_id_view_fragment.xml +++ b/OpenKeychain/src/main/res/layout/linked_id_view_fragment.xml @@ -132,11 +132,11 @@ android:layout_marginEnd="4dp" android:text="@string/add_keys_my_key" /> - - +