Use key icons in spinners

This commit is contained in:
Dominik Schürmann 2014-09-17 23:06:49 +02:00
parent b09d222f34
commit 9586d6b9b8
7 changed files with 157 additions and 86 deletions

View File

@ -56,6 +56,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ExportHelper; import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.KeyUpdateHelper; import org.sufficientlysecure.keychain.util.KeyUpdateHelper;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
@ -572,16 +573,10 @@ public class KeyListFragment extends LoaderFragment
// Note: order is important! // Note: order is important!
if (isRevoked) { if (isRevoked) {
h.mStatus.setImageDrawable( KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, KeyFormattingUtils.STATE_REVOKED);
getResources().getDrawable(R.drawable.status_signature_revoked_cutout));
h.mStatus.setColorFilter(getResources().getColor(R.color.android_red_dark),
PorterDuff.Mode.SRC_ATOP);
h.mStatus.setVisibility(View.VISIBLE); h.mStatus.setVisibility(View.VISIBLE);
} else if (isExpired) { } else if (isExpired) {
h.mStatus.setImageDrawable( KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, KeyFormattingUtils.STATE_EXPIRED);
getResources().getDrawable(R.drawable.status_signature_expired_cutout));
h.mStatus.setColorFilter(getResources().getColor(R.color.android_orange_dark),
PorterDuff.Mode.SRC_ATOP);
h.mStatus.setVisibility(View.VISIBLE); h.mStatus.setVisibility(View.VISIBLE);
} else if (isVerified) { } else if (isVerified) {
if (cursor.getInt(KeyListFragment.INDEX_HAS_ANY_SECRET) != 0) { if (cursor.getInt(KeyListFragment.INDEX_HAS_ANY_SECRET) != 0) {
@ -589,10 +584,7 @@ public class KeyListFragment extends LoaderFragment
h.mStatus.setVisibility(View.GONE); h.mStatus.setVisibility(View.GONE);
} else { } else {
// this is a public key - show if it's verified // this is a public key - show if it's verified
h.mStatus.setImageDrawable( KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, KeyFormattingUtils.STATE_VERIFIED);
getResources().getDrawable(R.drawable.status_signature_verified_cutout));
h.mStatus.setColorFilter(getResources().getColor(R.color.android_green_dark),
PorterDuff.Mode.SRC_ATOP);
h.mStatus.setVisibility(View.VISIBLE); h.mStatus.setVisibility(View.VISIBLE);
} }
} else { } else {

View File

@ -19,10 +19,14 @@
package org.sufficientlysecure.keychain.ui.util; package org.sufficientlysecure.keychain.ui.util;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PorterDuff;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.widget.ImageView;
import org.spongycastle.asn1.ASN1ObjectIdentifier; import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.nist.NISTNamedCurves; import org.spongycastle.asn1.nist.NISTNamedCurves;
@ -330,4 +334,41 @@ public class KeyFormattingUtils {
((int) digest[2] + 256) % 256}; ((int) digest[2] + 256) % 256};
} }
public static final int STATE_REVOKED = 1;
public static final int STATE_EXPIRED = 2;
public static final int STATE_VERIFIED = 3;
public static final int STATE_UNAVAILABLE = 4;
/**
* returns true if status has been set, if false no status!
*/
public static void setStatusImage(Context context, ImageView statusView, int state) {
switch (state) {
case STATE_REVOKED:
statusView.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout));
statusView.setColorFilter(context.getResources().getColor(R.color.android_red_dark),
PorterDuff.Mode.SRC_ATOP);
break;
case STATE_EXPIRED:
statusView.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_expired_cutout));
statusView.setColorFilter(context.getResources().getColor(R.color.android_orange_dark),
PorterDuff.Mode.SRC_ATOP);
break;
case STATE_UNAVAILABLE:
statusView.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout));
statusView.setColorFilter(context.getResources().getColor(R.color.bg_gray),
PorterDuff.Mode.SRC_ATOP);
break;
case STATE_VERIFIED:
statusView.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_verified_cutout));
statusView.setColorFilter(context.getResources().getColor(R.color.android_green_dark),
PorterDuff.Mode.SRC_ATOP);
break;
}
}
} }

View File

@ -24,10 +24,13 @@ import android.os.Bundle;
import android.support.v4.content.CursorLoader; import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.ImageView;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public class CertifyKeySpinner extends KeySpinner { public class CertifyKeySpinner extends KeySpinner {
private long mHiddenMasterKeyId = Constants.key.none; private long mHiddenMasterKeyId = Constants.key.none;
@ -61,22 +64,23 @@ public class CertifyKeySpinner extends KeySpinner {
KeychainContract.KeyRings.MASTER_KEY_ID, KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.KeyRings.KEY_ID, KeychainContract.KeyRings.KEY_ID,
KeychainContract.KeyRings.USER_ID, KeychainContract.KeyRings.USER_ID,
KeychainContract.KeyRings.IS_REVOKED,
KeychainContract.KeyRings.IS_EXPIRED, KeychainContract.KeyRings.IS_EXPIRED,
KeychainContract.KeyRings.HAS_CERTIFY, KeychainContract.KeyRings.HAS_CERTIFY,
KeychainContract.KeyRings.HAS_ANY_SECRET KeychainContract.KeyRings.HAS_ANY_SECRET
}; };
String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND "
+ KeychainContract.KeyRings.HAS_CERTIFY + " NOT NULL AND " + KeychainDatabase.Tables.KEYS + "." + KeychainContract.KeyRings.MASTER_KEY_ID
+ Tables.KEYS + "." + KeychainContract.KeyRings.IS_REVOKED + " = 0 AND " + " != " + mHiddenMasterKeyId;
+ 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 // Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed. // creating a Cursor for the data being displayed.
return new CursorLoader(getContext(), baseUri, projection, where, null, null); return new CursorLoader(getContext(), baseUri, projection, where, null, null);
} }
private int mIndexHasCertify, mIndexIsRevoked, mIndexIsExpired;
@Override @Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) { public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
super.onLoadFinished(loader, data); super.onLoadFinished(loader, data);
@ -84,6 +88,29 @@ public class CertifyKeySpinner extends KeySpinner {
if (mAdapter.getCount() == 2) { if (mAdapter.getCount() == 2) {
setSelection(1); setSelection(1);
} }
mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY);
mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
}
@Override
boolean setStatus(Context context, Cursor cursor, ImageView statusView) {
if (cursor.getInt(mIndexIsRevoked) != 0) {
KeyFormattingUtils.setStatusImage(getContext(), statusView, KeyFormattingUtils.STATE_REVOKED);
return false;
}
if (cursor.getInt(mIndexIsExpired) != 0) {
KeyFormattingUtils.setStatusImage(getContext(), statusView, KeyFormattingUtils.STATE_EXPIRED);
return false;
}
// don't invalidate the "None" entry, which is also null!
if (cursor.getPosition() != 0 && cursor.isNull(mIndexHasCertify)) {
KeyFormattingUtils.setStatusImage(getContext(), statusView, KeyFormattingUtils.STATE_UNAVAILABLE);
return false;
}
// valid key
return true;
} }
} }

View File

@ -29,6 +29,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.SpinnerAdapter; import android.widget.SpinnerAdapter;
import android.widget.TextView; import android.widget.TextView;
@ -139,21 +140,28 @@ public abstract class KeySpinner extends Spinner implements LoaderManager.Loader
@Override @Override
public void bindView(View view, Context context, Cursor cursor) { public void bindView(View view, Context context, Cursor cursor) {
TextView vKeyName = (TextView) view.findViewById(R.id.keyspinner_key_name);
ImageView vKeyStatus = (ImageView) view.findViewById(R.id.keyspinner_key_status);
TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email);
TextView vKeyId = (TextView) view.findViewById(R.id.keyspinner_key_id);
String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId)); String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId));
TextView vKeyName = ((TextView) view.findViewById(R.id.keyspinner_key_name));
TextView vKeyStatus = ((TextView) view.findViewById(R.id.keyspinner_key_status));
vKeyName.setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")")); vKeyName.setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")"));
((TextView) view.findViewById(R.id.keyspinner_key_email)).setText(userId[1]); vKeyEmail.setText(userId[1]);
((TextView) view.findViewById(R.id.keyspinner_key_id)).setText(KeyFormattingUtils.convertKeyIdToHex(cursor.getLong(mIndexKeyId))); vKeyId.setText(KeyFormattingUtils.convertKeyIdToHex(cursor.getLong(mIndexKeyId)));
String status = getStatus(getContext(), cursor);
if (status == null) { boolean valid = setStatus(getContext(), cursor, vKeyStatus);
if (valid) {
vKeyName.setTextColor(Color.BLACK); vKeyName.setTextColor(Color.BLACK);
vKeyEmail.setTextColor(Color.BLACK);
vKeyId.setTextColor(Color.BLACK);
vKeyStatus.setVisibility(View.GONE); vKeyStatus.setVisibility(View.GONE);
view.setClickable(false); view.setClickable(false);
} else { } else {
vKeyName.setTextColor(Color.GRAY); vKeyName.setTextColor(Color.GRAY);
vKeyEmail.setTextColor(Color.GRAY);
vKeyId.setTextColor(Color.GRAY);
vKeyStatus.setVisibility(View.VISIBLE); vKeyStatus.setVisibility(View.VISIBLE);
vKeyStatus.setText(status);
// this is a HACK. the trick is, if the element itself is clickable, the // this is a HACK. the trick is, if the element itself is clickable, the
// click is not passed on to the view list // click is not passed on to the view list
view.setClickable(true); view.setClickable(true);
@ -228,19 +236,19 @@ public abstract class KeySpinner extends Spinner implements LoaderManager.Loader
} }
((TextView) v.findViewById(R.id.keyspinner_key_name)).setText(R.string.choice_none); ((TextView) v.findViewById(R.id.keyspinner_key_name)).setText(R.string.choice_none);
v.findViewById(R.id.keyspinner_key_email).setVisibility(View.GONE); v.findViewById(R.id.keyspinner_key_email).setVisibility(View.GONE);
v.findViewById(R.id.keyspinner_key_row).setVisibility(View.GONE); v.findViewById(R.id.keyspinner_key_id).setVisibility(View.GONE);
v.findViewById(R.id.keyspinner_key_status).setVisibility(View.GONE);
} else { } else {
v = inner.getView(position - 1, convertView, parent); v = inner.getView(position - 1, convertView, parent);
v.findViewById(R.id.keyspinner_key_email).setVisibility(View.VISIBLE); v.findViewById(R.id.keyspinner_key_email).setVisibility(View.VISIBLE);
v.findViewById(R.id.keyspinner_key_row).setVisibility(View.VISIBLE); v.findViewById(R.id.keyspinner_key_id).setVisibility(View.VISIBLE);
} }
return v; return v;
} }
} }
/** Return a string which should be the disabled status of the key, or null if the key is enabled. */ boolean setStatus(Context context, Cursor cursor, ImageView statusView) {
String getStatus(Context context, Cursor cursor) { return true;
return null;
} }
} }

View File

@ -24,9 +24,11 @@ import android.os.Bundle;
import android.support.v4.content.CursorLoader; import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.ImageView;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public class SignKeySpinner extends KeySpinner { public class SignKeySpinner extends KeySpinner {
public SignKeySpinner(Context context) { public SignKeySpinner(Context context) {
@ -69,25 +71,30 @@ public class SignKeySpinner extends KeySpinner {
private int mIndexHasSign, mIndexIsRevoked, mIndexIsExpired; private int mIndexHasSign, mIndexIsRevoked, mIndexIsExpired;
@Override @Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
super.onLoadFinished(loader, cursor); super.onLoadFinished(loader, data);
mIndexHasSign = cursor.getColumnIndex(KeychainContract.KeyRings.HAS_SIGN); mIndexHasSign = data.getColumnIndex(KeychainContract.KeyRings.HAS_SIGN);
mIndexIsRevoked = cursor.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED); mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
mIndexIsExpired = cursor.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED); mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
} }
@Override @Override
String getStatus(Context context, Cursor cursor) { boolean setStatus(Context context, Cursor cursor, ImageView statusView) {
if (cursor.getInt(mIndexIsRevoked) != 0) { if (cursor.getInt(mIndexIsRevoked) != 0) {
return context.getString(R.string.revoked); KeyFormattingUtils.setStatusImage(getContext(), statusView, KeyFormattingUtils.STATE_REVOKED);
} return false;
if (cursor.getInt(mIndexHasSign) == 0) {
return context.getString(R.string.key_unavailable);
} }
if (cursor.getInt(mIndexIsExpired) != 0) { if (cursor.getInt(mIndexIsExpired) != 0) {
return context.getString(R.string.expired); KeyFormattingUtils.setStatusImage(getContext(), statusView, KeyFormattingUtils.STATE_EXPIRED);
return false;
} }
return null; if (cursor.getInt(mIndexHasSign) == 0) {
KeyFormattingUtils.setStatusImage(getContext(), statusView, KeyFormattingUtils.STATE_UNAVAILABLE);
return false;
}
// valid key
return true;
} }
} }

View File

@ -39,9 +39,9 @@
</LinearLayout> </LinearLayout>
<ImageView <ImageView
android:id="@+id/status_image"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/status_image"
android:layout_gravity="center" android:layout_gravity="center"
android:src="@drawable/status_signature_revoked_cutout" android:src="@drawable/status_signature_revoked_cutout"
android:padding="16dp" /> android:padding="16dp" />

View File

@ -1,63 +1,59 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:padding="4dp"
android:minHeight="24dp" android:minHeight="24dp"
android:layout_height="wrap_content"> android:gravity="center_vertical"
android:singleLine="true"
android:orientation="horizontal"
android:descendantFocusability="blocksDescendants"
android:focusable="false">
<LinearLayout
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:focusable="true"
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingRight="4dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<TextView <TextView
android:id="@+id/keyspinner_key_name" android:id="@+id/keyspinner_key_name"
android:textColor="?android:attr/textColorSecondary"
android:textSize="18sp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:text="@string/label_main_user_id"
android:ellipsize="end" /> android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView <TextView
android:id="@+id/keyspinner_key_email" android:id="@+id/keyspinner_key_email"
android:textColor="?android:attr/textColorTertiary"
android:textSize="14sp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="true"
android:ellipsize="end" android:ellipsize="end"
android:layout_marginTop="-4dip" android:text="user@example.com"
android:text="alice@example.com" /> android:textAppearance="?android:attr/textAppearanceSmall" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:padding="4dp"
android:minHeight="24dp"
android:layout_height="wrap_content"
android:id="@+id/keyspinner_key_row">
<TextView <TextView
android:id="@+id/keyspinner_key_id" android:id="@+id/keyspinner_key_id"
android:textColor="?android:attr/textColorTertiary" android:text="12345"
android:textSize="14sp" android:layout_width="wrap_content"
android:layout_width="0dip"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="true"
android:ellipsize="end" android:ellipsize="end"
android:typeface="monospace" android:typeface="monospace"
android:layout_marginTop="-4dip" android:textAppearance="?android:attr/textAppearanceSmall"/>
android:layout_weight="1"/> </LinearLayout>
<TextView <ImageView
android:id="@+id/keyspinner_key_status" android:id="@+id/keyspinner_key_status"
android:textColor="#e00"
android:textSize="12sp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:layout_gravity="center"
android:ellipsize="end" android:src="@drawable/status_signature_revoked_cutout"
android:layout_gravity="right" android:padding="16dp" />
android:text="status"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout> </LinearLayout>