certs: more filters, better initial uid selection, code cosmetics

- add three types of filters for certificate list (ui looks like crap
      there, need to work on that)
    - select uncertified uids by default in CertifyKeyActivity
    - move some code around in KeychainProvider, 's slightly less hacky now
This commit is contained in:
Vincent Breitmoser 2014-03-17 14:10:40 +01:00
parent f192c70179
commit e8ad4317a4
6 changed files with 87 additions and 55 deletions

View File

@ -82,6 +82,7 @@ public class KeychainProvider extends ContentProvider {
private static final int CERTS_BY_KEY_ROW_ID = 404; private static final int CERTS_BY_KEY_ROW_ID = 404;
private static final int CERTS_BY_KEY_ROW_ID_ALL = 405; private static final int CERTS_BY_KEY_ROW_ID_ALL = 405;
private static final int CERTS_BY_CERTIFIER_ID = 406; private static final int CERTS_BY_CERTIFIER_ID = 406;
private static final int CERTS_BY_KEY_ROW_ID_HAS_SECRET = 407;
// private static final int DATA_STREAM = 401; // private static final int DATA_STREAM = 401;
@ -256,6 +257,8 @@ public class KeychainProvider extends ContentProvider {
+ KeychainContract.PATH_BY_KEY_ROW_ID + "/#", CERTS_BY_KEY_ROW_ID); + KeychainContract.PATH_BY_KEY_ROW_ID + "/#", CERTS_BY_KEY_ROW_ID);
matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
+ KeychainContract.PATH_BY_KEY_ROW_ID + "/#/all", CERTS_BY_KEY_ROW_ID_ALL); + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/all", CERTS_BY_KEY_ROW_ID_ALL);
matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
+ KeychainContract.PATH_BY_KEY_ROW_ID + "/#/has_secret", CERTS_BY_KEY_ROW_ID_HAS_SECRET);
matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
+ KeychainContract.PATH_BY_KEY_ID + "/#", CERTS_BY_KEY_ID); + KeychainContract.PATH_BY_KEY_ID + "/#", CERTS_BY_KEY_ID);
matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
@ -456,6 +459,23 @@ public class KeychainProvider extends ContentProvider {
return projectionMap; return projectionMap;
} }
private HashMap<String, String> getProjectionMapForCerts() {
HashMap<String, String> pmap = new HashMap<String, String>();
pmap.put(Certs._ID, Tables.CERTS + "." + Certs._ID);
pmap.put(Certs.KEY_ID, Tables.CERTS + "." + Certs.KEY_ID);
pmap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK);
pmap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
pmap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
pmap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA);
pmap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
// verified key data
pmap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
// verifying key data
pmap.put("signer_uid", "signer." + UserIds.USER_ID + " AS signer_uid");
return pmap;
}
/** /**
* Builds default query for keyRings: KeyRings table is joined with UserIds and Keys * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
@ -521,7 +541,6 @@ public class KeychainProvider extends ContentProvider {
// all query() parameters, for good measure // all query() parameters, for good measure
String groupBy = null, having = null; String groupBy = null, having = null;
boolean all = false;
switch (match) { switch (match) {
case UNIFIED_KEY_RING: case UNIFIED_KEY_RING:
@ -709,7 +728,7 @@ public class KeychainProvider extends ContentProvider {
case CERTS_BY_ROW_ID: case CERTS_BY_ROW_ID:
case CERTS_BY_KEY_ROW_ID_ALL: case CERTS_BY_KEY_ROW_ID_ALL:
all = true; case CERTS_BY_KEY_ROW_ID_HAS_SECRET:
case CERTS_BY_KEY_ROW_ID: case CERTS_BY_KEY_ROW_ID:
qb.setTables(Tables.CERTS qb.setTables(Tables.CERTS
+ " JOIN " + Tables.USER_IDS + " ON (" + " JOIN " + Tables.USER_IDS + " ON ("
@ -719,10 +738,13 @@ public class KeychainProvider extends ContentProvider {
+ Tables.CERTS + "." + Certs.RANK + " = " + Tables.CERTS + "." + Certs.RANK + " = "
+ Tables.USER_IDS + "." + UserIds.RANK + Tables.USER_IDS + "." + UserIds.RANK
// noooooooot sure about this~ database design // noooooooot sure about this~ database design
+ ")" + (all ? " LEFT" : "") + ")" + (match == CERTS_BY_KEY_ROW_ID_ALL ? " LEFT" : "")
+ " JOIN " + Tables.KEYS + " ON (" + " JOIN " + Tables.KEYS + " ON ("
+ Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = "
+ Tables.KEYS + "." + Keys.KEY_ID + Tables.KEYS + "." + Keys.KEY_ID
+ (match == CERTS_BY_KEY_ROW_ID_HAS_SECRET ?
" AND " + Tables.KEYS + "." + Keys.TYPE + " = " + KeyTypes.SECRET : ""
)
+ ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON (" + ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON ("
+ Tables.KEYS + "." + Keys.KEY_RING_ROW_ID + " = " + Tables.KEYS + "." + Keys.KEY_RING_ROW_ID + " = "
+ "signer." + UserIds.KEY_RING_ROW_ID + "signer." + UserIds.KEY_RING_ROW_ID
@ -730,23 +752,11 @@ public class KeychainProvider extends ContentProvider {
+ "signer." + Keys.RANK + " = 0" + "signer." + Keys.RANK + " = 0"
+ ")"); + ")");
qb.setProjectionMap(getProjectionMapForCerts());
groupBy = Tables.CERTS + "." + Certs.RANK + ", " groupBy = Tables.CERTS + "." + Certs.RANK + ", "
+ Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER; + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER;
HashMap<String, String> pmap2 = new HashMap<String, String>();
pmap2.put(Certs._ID, Tables.CERTS + "." + Certs._ID);
pmap2.put(Certs.KEY_ID, Tables.CERTS + "." + Certs.KEY_ID);
pmap2.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK);
pmap2.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
pmap2.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
pmap2.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA);
pmap2.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
// verified key data
pmap2.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
// verifying key data
pmap2.put("signer_uid", "signer." + UserIds.USER_ID + " AS signer_uid");
qb.setProjectionMap(pmap2);
if(match == CERTS_BY_ROW_ID) { if(match == CERTS_BY_ROW_ID) {
qb.appendWhere(Tables.CERTS + "." + Certs._ID + " = "); qb.appendWhere(Tables.CERTS + "." + Certs._ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1)); qb.appendWhereEscapeString(uri.getPathSegments().get(1));
@ -867,10 +877,11 @@ public class KeychainProvider extends ContentProvider {
rowUri = ApiApps.buildIdUri(Long.toString(rowId)); rowUri = ApiApps.buildIdUri(Long.toString(rowId));
break; break;
case CERTS_BY_ROW_ID: case CERTS_BY_KEY_ROW_ID:
rowId = db.insertOrThrow(Tables.CERTS, null, values); rowId = db.insertOrThrow(Tables.CERTS, null, values);
// kinda useless.. should this be buildCertsByKeyRowIdUri? // kinda useless.. should this be buildCertsByKeyRowIdUri?
rowUri = Certs.buildCertsUri(Long.toString(rowId)); // rowUri = Certs.buildCertsUri(Long.toString(rowId));
rowUri = uri;
break; break;
default: default:

View File

@ -374,7 +374,7 @@ public class ProviderHelper {
values.put(Certs.VERIFIED, verified); values.put(Certs.VERIFIED, verified);
values.put(Certs.KEY_DATA, cert.getEncoded()); values.put(Certs.KEY_DATA, cert.getEncoded());
Uri uri = Certs.buildCertsUri(Long.toString(keyRingRowId)); Uri uri = Certs.buildCertsByKeyRowIdUri(Long.toString(keyRingRowId));
return ContentProviderOperation.newInsert(uri).withValues(values).build(); return ContentProviderOperation.newInsert(uri).withValues(values).build();
} }

View File

@ -31,8 +31,8 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.CheckBox; import android.widget.ArrayAdapter;
import android.widget.CompoundButton; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
@ -42,8 +42,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import com.beardedhen.androidbootstrap.BootstrapButton;
import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView; import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
@ -64,14 +62,14 @@ public class ViewKeyCertsFragment extends Fragment
// sort by our user id, // sort by our user id,
static final String SORT_ORDER = static final String SORT_ORDER =
KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.USER_ID + " ASC, " KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " ASC, "
+ KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.VERIFIED + " DESC, " + KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.VERIFIED + " DESC, "
+ "signer_uid ASC"; + "signer_uid ASC";
public static final String ARG_KEYRING_ROW_ID = "row_id"; public static final String ARG_KEYRING_ROW_ID = "row_id";
private StickyListHeadersListView mStickyList; private StickyListHeadersListView mStickyList;
private CheckBox mShowUnknown; private Spinner mSpinner;
private CertListAdapter mAdapter; private CertListAdapter mAdapter;
private boolean mUnknownShown = false; private boolean mUnknownShown = false;
@ -85,11 +83,18 @@ public class ViewKeyCertsFragment extends Fragment
return view; return view;
} }
private void toggleShowUnknown(boolean shown) { private void changeShowState(int type) {
if(shown) switch(type) {
mDataUri = mBaseUri.buildUpon().appendPath("all").build(); case 0:
else mDataUri = mBaseUri.buildUpon().appendPath("has_secret").build();
mDataUri = mBaseUri; break;
case 1:
mDataUri = mBaseUri;
break;
case 2:
mDataUri = mBaseUri.buildUpon().appendPath("all").build();
break;
}
getLoaderManager().restartLoader(0, null, this); getLoaderManager().restartLoader(0, null, this);
} }
@ -97,11 +102,24 @@ public class ViewKeyCertsFragment extends Fragment
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
mShowUnknown = (CheckBox) getActivity().findViewById(R.id.showUnknown); mSpinner = (Spinner) getActivity().findViewById(R.id.spinner);
mShowUnknown.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_spinner_item, new String[] {
getResources().getString(R.string.certs_list_known_secret),
getResources().getString(R.string.certs_list_known),
getResources().getString(R.string.certs_list_all)
} );
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinner.setAdapter(adapter);
mSpinner.setSelection(1);
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) { public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
toggleShowUnknown(b); changeShowState(i);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
} }
}); });

View File

@ -63,8 +63,11 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
int count = newCursor.getCount(); int count = newCursor.getCount();
mCheckStates.ensureCapacity(count); mCheckStates.ensureCapacity(count);
// initialize to true (use case knowledge: we usually want to sign all uids) // initialize to true (use case knowledge: we usually want to sign all uids)
for(int i = 0; i < count; i++) for(int i = 0; i < count; i++) {
mCheckStates.add(true); newCursor.moveToPosition(i);
int verified = newCursor.getInt(mVerifiedId);
mCheckStates.add(verified == 0);
}
} }
} }
@ -116,7 +119,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.checkBox); final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.checkBox);
final int position = cursor.getPosition(); final int position = cursor.getPosition();
vCheckBox.setClickable(false); vCheckBox.setOnCheckedChangeListener(null);
vCheckBox.setChecked(mCheckStates.get(position)); vCheckBox.setChecked(mCheckStates.get(position));
vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override @Override

View File

@ -9,29 +9,26 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/spinner"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:layout_centerHorizontal="true"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp" />
<view <view
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
class="se.emilsjolander.stickylistheaders.StickyListHeadersListView" class="se.emilsjolander.stickylistheaders.StickyListHeadersListView"
android:id="@+id/list" android:id="@+id/list"
android:layout_alignParentTop="true"
android:layout_above="@+id/showUnknown"
android:paddingRight="32dp" android:paddingRight="32dp"
android:layout_alignParentLeft="true" android:paddingLeft="16dp"
android:layout_alignParentStart="true" android:layout_alignParentStart="false"
android:paddingLeft="16dp" /> android:layout_alignParentEnd="false"
android:layout_below="@+id/spinner" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/show_unknown_signatures"
android:id="@+id/showUnknown"
android:enabled="true"
android:layout_alignParentBottom="true"
android:layout_alignEnd="@+id/list"
android:singleLine="false"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -480,5 +480,8 @@
<string name="empty_certs">No certificates for this key</string> <string name="empty_certs">No certificates for this key</string>
<string name="section_uids_to_sign">User IDs to sign</string> <string name="section_uids_to_sign">User IDs to sign</string>
<string name="progress_re_adding_certs">Reapplying certificates</string> <string name="progress_re_adding_certs">Reapplying certificates</string>
<string name="certs_list_known_secret">Show by known secret keys</string>
<string name="certs_list_known">Show by known public keys</string>
<string name="certs_list_all">Show all certificates</string>
</resources> </resources>