From ef2449917e53e312ebc2eb4bea7e3149ba9db137 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 14 Aug 2014 12:22:02 +0200 Subject: [PATCH 1/7] factor KeySpinner out --- .../ui/EncryptAsymmetricFragment.java | 142 +-------------- .../ui/widget/EncryptKeyCompletionView.java | 4 +- .../keychain/ui/widget/KeySpinner.java | 171 ++++++++++++++++++ .../keychain/ui/widget/SignKeySpinner.java | 48 +++++ .../layout/encrypt_asymmetric_fragment.xml | 2 +- ...mmetric_signkey.xml => keyspinner_key.xml} | 0 6 files changed, 230 insertions(+), 137 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java rename OpenKeychain/src/main/res/layout/{encrypt_asymmetric_signkey.xml => keyspinner_key.xml} (100%) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java index a402b6f68..41566cffc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java @@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView; +import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -54,22 +55,20 @@ import java.util.Iterator; import java.util.List; public class EncryptAsymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener { - public static final String ARG_SIGNATURE_KEY_ID = "signature_key_id"; - public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids"; - ProviderHelper mProviderHelper; // view - private Spinner mSign; + private KeySpinner mSign; private EncryptKeyCompletionView mEncryptKeyView; - private SelectSignKeyCursorAdapter mSignAdapter = new SelectSignKeyCursorAdapter(); // model private EncryptActivityInterface mEncryptInterface; @Override public void onNotifyUpdate() { - + if (mSign != null) { + mSign.setSelectedKeyId(mEncryptInterface.getSignatureKey()); + } } @Override @@ -101,8 +100,7 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false); - mSign = (Spinner) view.findViewById(R.id.sign); - mSign.setAdapter(mSignAdapter); + mSign = (KeySpinner) view.findViewById(R.id.sign); mSign.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { @@ -128,42 +126,6 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi // preselect keys given preselectKeys(); - getLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks() { - @Override - public Loader onCreateLoader(int id, Bundle args) { - // 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 = KeyRings.buildUnifiedKeyRingsUri(); - - // These are the rows that we will retrieve. - String[] projection = new String[]{ - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - KeyRings.KEY_ID, - KeyRings.USER_ID, - KeyRings.IS_EXPIRED, - KeyRings.HAS_SIGN, - KeyRings.HAS_ANY_SECRET - }; - - String where = KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeyRings.HAS_SIGN + " NOT NULL AND " - + KeyRings.IS_REVOKED + " = 0 AND " + KeyRings.IS_EXPIRED + " = 0"; - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, projection, where, null, null); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - mSignAdapter.swapCursor(data); - } - - @Override - public void onLoaderReset(Loader loader) { - mSignAdapter.swapCursor(null); - } - }); mEncryptKeyView.setTokenListener(new TokenCompleteTextView.TokenListener() { @Override public void onTokenAdded(Object token) { @@ -194,6 +156,7 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi KeyRings.buildUnifiedKeyRingUri(signatureKey)); if(keyring.hasAnySecret()) { setSignatureKeyId(keyring.getMasterKeyId()); + mSign.setSelectedKeyId(mEncryptInterface.getSignatureKey()); } } catch (PgpGeneralException e) { Log.e(Constants.TAG, "key not found!", e); @@ -233,95 +196,4 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi setEncryptionKeyIds(keyIdsArr); setEncryptionUserIds(userIds.toArray(new String[userIds.size()])); } - - private class SelectSignKeyCursorAdapter extends BaseAdapter implements SpinnerAdapter { - private CursorAdapter inner; - private int mIndexUserId; - private int mIndexKeyId; - private int mIndexMasterKeyId; - - public SelectSignKeyCursorAdapter() { - inner = new CursorAdapter(null, null, 0) { - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return getActivity().getLayoutInflater().inflate(R.layout.encrypt_asymmetric_signkey, null); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId)); - ((TextView) view.findViewById(android.R.id.title)).setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")")); - ((TextView) view.findViewById(android.R.id.text1)).setText(userId[1]); - ((TextView) view.findViewById(android.R.id.text2)).setText(PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId))); - } - - @Override - public long getItemId(int position) { - mCursor.moveToPosition(position); - return mCursor.getLong(mIndexMasterKeyId); - } - }; - } - - public Cursor swapCursor(Cursor newCursor) { - if (newCursor == null) return inner.swapCursor(null); - - mIndexKeyId = newCursor.getColumnIndex(KeyRings.KEY_ID); - mIndexUserId = newCursor.getColumnIndex(KeyRings.USER_ID); - mIndexMasterKeyId = newCursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - if (newCursor.moveToFirst()) { - do { - if (newCursor.getLong(mIndexMasterKeyId) == mEncryptInterface.getSignatureKey()) { - mSign.setSelection(newCursor.getPosition() + 1); - } - } while (newCursor.moveToNext()); - } - return inner.swapCursor(newCursor); - } - - @Override - public int getCount() { - return inner.getCount() + 1; - } - - @Override - public Object 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) { - View v = getDropDownView(position, convertView, parent); - v.findViewById(android.R.id.text1).setVisibility(View.GONE); - return v; - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - View v; - if (position == 0) { - if (convertView == null) { - v = inner.newView(null, null, parent); - } else { - v = convertView; - } - ((TextView) v.findViewById(android.R.id.title)).setText("None"); - v.findViewById(android.R.id.text1).setVisibility(View.GONE); - v.findViewById(android.R.id.text2).setVisibility(View.GONE); - } else { - v = inner.getView(position - 1, convertView, parent); - v.findViewById(android.R.id.text1).setVisibility(View.VISIBLE); - v.findViewById(android.R.id.text2).setVisibility(View.VISIBLE); - } - return v; - } - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index 20b9570bb..ceb3f665f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -111,7 +111,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView { protected void onAttachedToWindow() { super.onAttachedToWindow(); if (getContext() instanceof FragmentActivity) { - ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks() { + ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(hashCode(), null, new LoaderManager.LoaderCallbacks() { @Override public Loader onCreateLoader(int id, Bundle args) { // These are the rows that we will retrieve. @@ -143,6 +143,8 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView { swapCursor(null); } }); + } else { + Log.e(Constants.TAG, "EncryptKeyCompletionView must be attached to a FragmentActivity, this is " + getContext().getClass()); } } 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 new file mode 100644 index 000000000..b8d83311a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -0,0 +1,171 @@ +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.database.Cursor; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; +import android.support.v4.widget.CursorAdapter; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.util.Log; + +public abstract class KeySpinner extends Spinner { + private long mSelectedKeyId; + private SelectKeyAdapter mAdapter = new SelectKeyAdapter(); + + public KeySpinner(Context context) { + super(context); + } + + public KeySpinner(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public KeySpinner(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public abstract Loader onCreateLoader(); + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + setAdapter(mAdapter); + if (getContext() instanceof FragmentActivity) { + ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(hashCode(), null, new LoaderManager.LoaderCallbacks() { + @Override + public Loader onCreateLoader(int id, Bundle args) { + return KeySpinner.this.onCreateLoader(); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + mAdapter.swapCursor(data); + } + + @Override + public void onLoaderReset(Loader loader) { + mAdapter.swapCursor(null); + } + }); + } else { + Log.e(Constants.TAG, "KeySpinner must be attached to FragmentActivity, this is " + getContext().getClass()); + } + } + + public long getSelectedKeyId() { + return mSelectedKeyId; + } + + public void setSelectedKeyId(long selectedKeyId) { + this.mSelectedKeyId = selectedKeyId; + } + + private class SelectKeyAdapter extends BaseAdapter implements SpinnerAdapter { + private CursorAdapter inner; + private int mIndexUserId; + private int mIndexKeyId; + private int mIndexMasterKeyId; + + public SelectKeyAdapter() { + inner = new CursorAdapter(null, null, 0) { + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return View.inflate(getContext(), R.layout.keyspinner_key, null); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId)); + ((TextView) view.findViewById(android.R.id.title)).setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")")); + ((TextView) view.findViewById(android.R.id.text1)).setText(userId[1]); + ((TextView) view.findViewById(android.R.id.text2)).setText(PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId))); + } + + @Override + public long getItemId(int position) { + mCursor.moveToPosition(position); + return mCursor.getLong(mIndexMasterKeyId); + } + }; + } + + public Cursor swapCursor(Cursor newCursor) { + if (newCursor == null) return inner.swapCursor(null); + + mIndexKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.KEY_ID); + mIndexUserId = newCursor.getColumnIndex(KeychainContract.KeyRings.USER_ID); + mIndexMasterKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID); + if (newCursor.moveToFirst()) { + do { + if (newCursor.getLong(mIndexMasterKeyId) == mSelectedKeyId) { + setSelection(newCursor.getPosition() + 1); + } + } while (newCursor.moveToNext()); + } + return inner.swapCursor(newCursor); + } + + @Override + public int getCount() { + return inner.getCount() + 1; + } + + @Override + public Object 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) { + try { + View v = getDropDownView(position, convertView, parent); + v.findViewById(android.R.id.text1).setVisibility(View.GONE); + return v; + } catch (NullPointerException e) { + // This is for the preview... + return View.inflate(getContext(), android.R.layout.simple_list_item_1, null); + } + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + View v; + if (position == 0) { + if (convertView == null) { + v = inner.newView(null, null, parent); + } else { + v = convertView; + } + ((TextView) v.findViewById(android.R.id.title)).setText("None"); + v.findViewById(android.R.id.text1).setVisibility(View.GONE); + v.findViewById(android.R.id.text2).setVisibility(View.GONE); + } else { + v = inner.getView(position - 1, convertView, parent); + v.findViewById(android.R.id.text1).setVisibility(View.VISIBLE); + v.findViewById(android.R.id.text2).setVisibility(View.VISIBLE); + } + return v; + } + } +} 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 new file mode 100644 index 000000000..cbec7f920 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java @@ -0,0 +1,48 @@ +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.util.AttributeSet; +import org.sufficientlysecure.keychain.provider.KeychainContract; + +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() { + // 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(); + + // These are the rows that we will retrieve. + String[] projection = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.KEY_ID, + KeychainContract.KeyRings.USER_ID, + KeychainContract.KeyRings.IS_EXPIRED, + KeychainContract.KeyRings.HAS_SIGN, + KeychainContract.KeyRings.HAS_ANY_SECRET + }; + + String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeychainContract.KeyRings.HAS_SIGN + " NOT NULL AND " + + KeychainContract.KeyRings.IS_REVOKED + " = 0 AND " + KeychainContract.KeyRings.IS_EXPIRED + " = 0"; + + // 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); + } +} diff --git a/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml b/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml index 4d82477bc..2189a1f34 100644 --- a/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml +++ b/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml @@ -24,7 +24,7 @@ android:textAppearance="?android:attr/textAppearanceMedium" android:text="@string/label_asymmetric_from" /> - Date: Thu, 14 Aug 2014 12:22:44 +0200 Subject: [PATCH 2/7] add HAS_CERTIFY (not sure why it's missing) --- .../keychain/provider/KeychainContract.java | 1 + .../keychain/provider/KeychainProvider.java | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index dd59f8603..c239ea7f7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -111,6 +111,7 @@ public class KeychainContract { public static final String HAS_ANY_SECRET = "has_any_secret"; public static final String HAS_ENCRYPT = "has_encrypt"; public static final String HAS_SIGN = "has_sign"; + public static final String HAS_CERTIFY = "has_certify"; public static final String PUBKEY_DATA = "pubkey_data"; public static final String PRIVKEY_DATA = "privkey_data"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index c914cb5b7..ed12fb94a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -271,6 +271,8 @@ public class KeychainProvider extends ContentProvider { "kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT); projectionMap.put(KeyRings.HAS_SIGN, "kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN); + projectionMap.put(KeyRings.HAS_CERTIFY, + "kC." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN); projectionMap.put(KeyRings.IS_EXPIRED, "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS + "." + Keys.EXPIRY + " < " + new Date().getTime() / 1000 + ") AS " + KeyRings.IS_EXPIRED); @@ -324,6 +326,15 @@ public class KeychainProvider extends ContentProvider { + " AND ( kS." + Keys.EXPIRY + " IS NULL OR kS." + Keys.EXPIRY + " >= " + new Date().getTime() / 1000 + " )" + ")" : "") + + (plist.contains(KeyRings.HAS_CERTIFY) ? + " LEFT JOIN " + Tables.KEYS + " AS kC ON (" + +"kC." + Keys.MASTER_KEY_ID + + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + + " AND kC." + Keys.IS_REVOKED + " = 0" + + " AND kC." + Keys.CAN_CERTIFY + " = 1" + + " AND ( kC." + Keys.EXPIRY + " IS NULL OR kC." + Keys.EXPIRY + + " >= " + new Date().getTime() / 1000 + " )" + + ")" : "") ); qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0"); // in case there are multiple verifying certificates From 07d6a26778b69c5340f5bbf11dcab970c63783d4 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 14 Aug 2014 15:23:12 +0200 Subject: [PATCH 3/7] add OnKeyChangedListener to KeySpinner --- .../ui/EncryptAsymmetricFragment.java | 11 ++--- .../keychain/ui/widget/KeySpinner.java | 41 +++++++++++++++++-- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java index 41566cffc..748cbca14 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java @@ -101,15 +101,10 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false); mSign = (KeySpinner) view.findViewById(R.id.sign); - mSign.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + mSign.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() { @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - setSignatureKeyId(parent.getAdapter().getItemId(position)); - } - - @Override - public void onNothingSelected(AdapterView parent) { - setSignatureKeyId(Constants.key.none); + public void onKeyChanged(long masterKeyId) { + setSignatureKeyId(masterKeyId); } }); mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list); 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 b8d83311a..fe3c61197 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 @@ -10,6 +10,7 @@ import android.support.v4.widget.CursorAdapter; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Spinner; import android.widget.SpinnerAdapter; @@ -23,27 +24,62 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.util.Log; public abstract class KeySpinner extends Spinner { + public interface OnKeyChangedListener { + public void onKeyChanged(long masterKeyId); + } + private long mSelectedKeyId; private SelectKeyAdapter mAdapter = new SelectKeyAdapter(); + private OnKeyChangedListener mListener; public KeySpinner(Context context) { super(context); + initView(); } public KeySpinner(Context context, AttributeSet attrs) { super(context, attrs); + initView(); } public KeySpinner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + initView(); + } + + private void initView() { + setAdapter(mAdapter); + super.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (mListener != null) { + mListener.onKeyChanged(id); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + if (mListener != null) { + mListener.onKeyChanged(Constants.key.none); + } + } + }); } public abstract Loader onCreateLoader(); + @Override + public void setOnItemSelectedListener(OnItemSelectedListener listener) { + throw new UnsupportedOperationException(); + } + + public void setOnKeyChangedListener(OnKeyChangedListener listener) { + mListener = listener; + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - setAdapter(mAdapter); if (getContext() instanceof FragmentActivity) { ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(hashCode(), null, new LoaderManager.LoaderCallbacks() { @Override @@ -97,8 +133,7 @@ public abstract class KeySpinner extends Spinner { @Override public long getItemId(int position) { - mCursor.moveToPosition(position); - return mCursor.getLong(mIndexMasterKeyId); + return ((Cursor) getItem(position)).getLong(mIndexMasterKeyId); } }; } From 00286744f232bec569b46411db7be62e40c534d0 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 14 Aug 2014 15:44:54 +0200 Subject: [PATCH 4/7] small bug in has_certify --- .../sufficientlysecure/keychain/provider/KeychainProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index ed12fb94a..2c552a060 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -272,7 +272,7 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(KeyRings.HAS_SIGN, "kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN); projectionMap.put(KeyRings.HAS_CERTIFY, - "kC." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN); + "kC." + Keys.KEY_ID + " AS " + KeyRings.HAS_CERTIFY); projectionMap.put(KeyRings.IS_EXPIRED, "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS + "." + Keys.EXPIRY + " < " + new Date().getTime() / 1000 + ") AS " + KeyRings.IS_EXPIRED); From a6118877ff1b096925c908583985889d30087e24 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 14 Aug 2014 16:00:07 +0200 Subject: [PATCH 5/7] Add and use CertifyKeySpinner --- .../keychain/ui/CertifyKeyActivity.java | 32 ++++++------- .../keychain/ui/widget/CertifyKeySpinner.java | 48 +++++++++++++++++++ .../main/res/layout/certify_key_activity.xml | 12 ++--- 3 files changed, 66 insertions(+), 26 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index c1986825c..d1e8a80dc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -34,6 +34,7 @@ import android.support.v7.app.ActionBarActivity; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; +import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -56,6 +57,8 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; +import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; +import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Notify; @@ -64,18 +67,17 @@ import java.util.ArrayList; /** * Signs the specified public key with the specified secret master key */ -public class CertifyKeyActivity extends ActionBarActivity implements - SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks { +public class CertifyKeyActivity extends ActionBarActivity implements LoaderManager.LoaderCallbacks { private View mCertifyButton; private ImageView mActionCertifyImage; private CheckBox mUploadKeyCheckbox; private Spinner mSelectKeyserverSpinner; - private SelectSecretKeyLayoutFragment mSelectKeyFragment; + private CertifyKeySpinner mCertifyKeySpinner; private Uri mDataUri; - private long mPubKeyId = 0; - private long mMasterKeyId = 0; + private long mPubKeyId = Constants.key.none; + private long mMasterKeyId = Constants.key.none; private ListView mUserIds; private UserIdsAdapter mUserIdsAdapter; @@ -89,8 +91,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements setContentView(R.layout.certify_key_activity); - mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getSupportFragmentManager() - .findFragmentById(R.id.sign_key_select_key_fragment); + mCertifyKeySpinner = (CertifyKeySpinner) findViewById(R.id.certify_key_spinner); mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver); mUploadKeyCheckbox = (CheckBox) findViewById(R.id.sign_key_upload_checkbox); mCertifyButton = findViewById(R.id.certify_key_certify_button); @@ -101,8 +102,12 @@ public class CertifyKeyActivity extends ActionBarActivity implements mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), PorterDuff.Mode.SRC_IN); - mSelectKeyFragment.setCallback(this); - mSelectKeyFragment.setFilterCertify(true); + mCertifyKeySpinner.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() { + @Override + public void onKeyChanged(long masterKeyId) { + mMasterKeyId = masterKeyId; + } + }); ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, Preferences.getPreferences(this) @@ -135,7 +140,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements public void onClick(View v) { if (mPubKeyId != 0) { if (mMasterKeyId == 0) { - mSelectKeyFragment.setError(getString(R.string.select_key_to_certify)); Notify.showNotify(CertifyKeyActivity.this, getString(R.string.select_key_to_certify), Notify.Style.ERROR); } else { @@ -367,14 +371,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements startService(intent); } - /** - * callback from select key fragment - */ - @Override - public void onKeySelected(long secretKeyId) { - mMasterKeyId = secretKeyId; - } - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { 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 new file mode 100644 index 000000000..b686d108b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java @@ -0,0 +1,48 @@ +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.util.AttributeSet; +import org.sufficientlysecure.keychain.provider.KeychainContract; + +public class CertifyKeySpinner extends KeySpinner { + 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); + } + + @Override + public Loader onCreateLoader() { + // 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(); + + // These are the rows that we will retrieve. + String[] projection = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.KEY_ID, + KeychainContract.KeyRings.USER_ID, + KeychainContract.KeyRings.IS_EXPIRED, + KeychainContract.KeyRings.HAS_CERTIFY, + KeychainContract.KeyRings.HAS_ANY_SECRET + }; + + String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeychainContract.KeyRings.HAS_CERTIFY + " NOT NULL AND " + + KeychainContract.KeyRings.IS_REVOKED + " = 0 AND " + KeychainContract.KeyRings.IS_EXPIRED + " = 0"; + + // 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); + } +} diff --git a/OpenKeychain/src/main/res/layout/certify_key_activity.xml b/OpenKeychain/src/main/res/layout/certify_key_activity.xml index 34d4dbd57..bce194438 100644 --- a/OpenKeychain/src/main/res/layout/certify_key_activity.xml +++ b/OpenKeychain/src/main/res/layout/certify_key_activity.xml @@ -26,14 +26,10 @@ android:layout_marginTop="14dp" android:text="@string/section_certification_key" /> - + Date: Thu, 14 Aug 2014 16:14:16 +0200 Subject: [PATCH 6/7] Fix KeySpinner on cursor change --- .../sufficientlysecure/keychain/ui/widget/KeySpinner.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 fe3c61197..380361fc0 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 @@ -133,7 +133,12 @@ public abstract class KeySpinner extends Spinner { @Override public long getItemId(int position) { - return ((Cursor) getItem(position)).getLong(mIndexMasterKeyId); + try { + return ((Cursor) getItem(position)).getLong(mIndexMasterKeyId); + } catch (Exception e) { + // This can happen on concurrent modification :( + return Constants.key.none; + } } }; } From e1958009bd022021971c1fd2f81557fb4fe99e4e Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 14 Aug 2014 18:10:22 +0200 Subject: [PATCH 7/7] Do not allow self certifying --- .../keychain/ui/CertifyKeyActivity.java | 2 +- .../keychain/ui/widget/CertifyKeySpinner.java | 16 ++++++++++++++-- .../keychain/ui/widget/KeySpinner.java | 6 +++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index d1e8a80dc..7c6e94d5e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -34,7 +34,6 @@ import android.support.v7.app.ActionBarActivity; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; -import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -203,6 +202,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements LoaderManag if (data.moveToFirst()) { // TODO: put findViewById in onCreate! mPubKeyId = data.getLong(INDEX_MASTER_KEY_ID); + mCertifyKeySpinner.setHiddenMasterKeyId(mPubKeyId); String keyIdStr = PgpKeyHelper.convertKeyIdToHex(mPubKeyId); ((TextView) findViewById(R.id.key_id)).setText(keyIdStr); 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 index b686d108b..030a76136 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java @@ -6,9 +6,13 @@ import android.net.Uri; 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.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; public class CertifyKeySpinner extends KeySpinner { + private long mHiddenMasterKeyId = Constants.key.none; + public CertifyKeySpinner(Context context) { super(context); } @@ -21,6 +25,11 @@ public class CertifyKeySpinner extends KeySpinner { super(context, attrs, defStyle); } + public void setHiddenMasterKeyId(long hiddenMasterKeyId) { + this.mHiddenMasterKeyId = hiddenMasterKeyId; + reload(); + } + @Override public Loader onCreateLoader() { // This is called when a new Loader needs to be created. This @@ -38,8 +47,11 @@ public class CertifyKeySpinner extends KeySpinner { KeychainContract.KeyRings.HAS_ANY_SECRET }; - String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeychainContract.KeyRings.HAS_CERTIFY + " NOT NULL AND " - + KeychainContract.KeyRings.IS_REVOKED + " = 0 AND " + KeychainContract.KeyRings.IS_EXPIRED + " = 0"; + String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " + + KeychainContract.KeyRings.HAS_CERTIFY + " NOT NULL AND " + + KeychainContract.KeyRings.IS_REVOKED + " = 0 AND " + + KeychainContract.KeyRings.IS_EXPIRED + " = 0 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. 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 380361fc0..b12029239 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 @@ -80,8 +80,12 @@ public abstract class KeySpinner extends Spinner { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + reload(); + } + + public void reload() { if (getContext() instanceof FragmentActivity) { - ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(hashCode(), null, new LoaderManager.LoaderCallbacks() { + ((FragmentActivity) getContext()).getSupportLoaderManager().restartLoader(hashCode(), null, new LoaderManager.LoaderCallbacks() { @Override public Loader onCreateLoader(int id, Bundle args) { return KeySpinner.this.onCreateLoader();