diff options
author | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-06-17 19:56:13 +0200 |
---|---|---|
committer | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-06-17 19:56:13 +0200 |
commit | 300fd8e0f26c328aa351318fdea17485d2d940d4 (patch) | |
tree | d028024da0370f50eeed4bb1a76cf9c1a7baeb36 /OpenKeychain/src/main | |
parent | 0b3317600bf7641425cfacb9709333c11a897414 (diff) | |
parent | 04d2b6a5076a1a7264687999152f8c24ece773ab (diff) | |
download | open-keychain-300fd8e0f26c328aa351318fdea17485d2d940d4.tar.gz open-keychain-300fd8e0f26c328aa351318fdea17485d2d940d4.tar.bz2 open-keychain-300fd8e0f26c328aa351318fdea17485d2d940d4.zip |
Merge pull request #1352 from open-keychain/v/instrument
instrumentation branch
Diffstat (limited to 'OpenKeychain/src/main')
26 files changed, 300 insertions, 371 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java index fa3600ffb..2f2838f70 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java @@ -74,6 +74,10 @@ public class ClipboardReflection { Method methodGetPrimaryClip = clipboard.getClass().getMethod("getPrimaryClip"); Object clipData = methodGetPrimaryClip.invoke(clipboard); + if (clipData == null) { + return null; + } + // ClipData.Item clipDataItem = clipData.getItemAt(0); Method methodGetItemAt = clipData.getClass().getMethod("getItemAt", int.class); Object clipDataItem = methodGetItemAt.invoke(clipData, 0); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java index 469e386cb..da0aef018 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java @@ -82,7 +82,7 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> { CanonicalizedSecretKeyRing secRing = mProviderHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); - modifyResult = keyOperations.modifySecretKeyRing(secRing, cryptoInput, saveParcel, log, 2); + modifyResult = keyOperations.modifySecretKeyRing(secRing, cryptoInput, saveParcel); if (modifyResult.isPending()) { return modifyResult; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 707cf0af1..8c68bda19 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -854,7 +854,11 @@ public abstract class OperationResult implements Parcelable { if (mParcels.isEmpty()) { return null; } - return mParcels.get(mParcels.size() -1); + LogEntryParcel last = mParcels.get(mParcels.size() -1); + if (last instanceof SubLogEntryParcel) { + return ((SubLogEntryParcel) last).getSubResult().getLog().getLast(); + } + return last; } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index fd3d41f93..e3059defb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -149,7 +149,7 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel> return verifySignedLiteralData(input, aIn, outputStream, 0); } else if (aIn.isClearText()) { // a cleartext signature, verify it with the other method - return verifyCleartextSignature(aIn, 0); + return verifyCleartextSignature(aIn, outputStream, 0); } else { // else: ascii armored encryption! go on... return decryptVerify(input, cryptoInput, in, outputStream, 0); @@ -757,6 +757,12 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel> // TODO: slow annealing to fake a progress? } + metadata = new OpenPgpMetadata( + originalFilename, + mimeType, + literalData.getModificationTime().getTime(), + alreadyWritten); + if (signature != null) { updateProgress(R.string.progress_verifying_signature, 90, 100); log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); @@ -833,7 +839,7 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel> * pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java */ private DecryptVerifyResult verifyCleartextSignature( - ArmoredInputStream aIn, int indent) throws IOException, PGPException { + ArmoredInputStream aIn, OutputStream outputStream, int indent) throws IOException, PGPException { OperationLog log = new OperationLog(); @@ -863,8 +869,9 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel> out.close(); byte[] clearText = out.toByteArray(); - if (out != null) { - out.write(clearText); + if (outputStream != null) { + outputStream.write(clearText); + outputStream.close(); } updateProgress(R.string.progress_processing_signature, 60, 100); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 1da13023d..a018815f3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -365,14 +365,9 @@ public class PgpKeyOperation { public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, CryptoInputParcel cryptoInput, SaveKeyringParcel saveParcel) { - return modifySecretKeyRing(wsKR, cryptoInput, saveParcel, new OperationLog(), 0); - } - public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, - CryptoInputParcel cryptoInput, - SaveKeyringParcel saveParcel, - OperationLog log, - int indent) { + OperationLog log = new OperationLog(); + int indent = 0; /* * 1. Unlock private key diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 8253801d9..36ba47672 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -179,7 +179,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { + Tables.API_APPS + "(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") ON DELETE CASCADE" + ")"; - KeychainDatabase(Context context) { + public KeychainDatabase(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); mContext = context; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 72100677c..dbbfe3133 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -149,6 +149,14 @@ public class PassphraseCacheService extends Service { context.startService(intent); } + public static void clearCachedPassphrases(Context context) { + Log.d(Constants.TAG, "PassphraseCacheService.clearCachedPassphrase()"); + + Intent intent = new Intent(context, PassphraseCacheService.class); + intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); + + context.startService(intent); + } /** * Gets a cached passphrase from memory by sending an intent to the service. This method is diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java index 1dcda5b8d..051da5d6b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -153,7 +153,9 @@ public class DecryptTextFragment extends DecryptFragment { @Override protected PgpDecryptVerifyInputParcel createOperationInput() { - return new PgpDecryptVerifyInputParcel(mCiphertext.getBytes()); + PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(mCiphertext.getBytes()); + input.setAllowSymmetricDecryption(true); + return input; } @Override 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 1a23dda93..355c649e7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java @@ -75,7 +75,6 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false); - mSignKeySpinner = (KeySpinner) view.findViewById(R.id.sign); mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list); mEncryptKeyView.setThreshold(1); // Start working from first character @@ -168,7 +167,7 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment { @Override public long getAsymmetricSigningKeyId() { - return mSignKeySpinner.getSelectedItemId(); + return mSignKeySpinner.getSelectedKeyId(); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index 83fede917..d93b52453 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -240,7 +240,7 @@ public class EncryptTextFragment boolean gotEncryptionKeys = (encryptionKeyIds != null && encryptionKeyIds.length > 0); - if (!gotEncryptionKeys && signingKeyId == 0L) { + if (!gotEncryptionKeys && signingKeyId == Constants.key.none) { Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR) .show(this); return null; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index f5a909676..a0f6d0e1b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -51,6 +51,8 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac private static final int ID_SETTINGS = 4; private static final int ID_HELP = 5; + public static final String EXTRA_SKIP_FIRST_TIME = "skip_first_time"; + public Drawer.Result mDrawerResult; private Toolbar mToolbar; @@ -114,7 +116,7 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac // if this is the first time show first time activity Preferences prefs = Preferences.getPreferences(this); - if (prefs.isFirstTime()) { + if (!getIntent().getBooleanExtra(EXTRA_SKIP_FIRST_TIME, false) && prefs.isFirstTime()) { Intent intent = new Intent(this, CreateKeyActivity.class); intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true); startActivity(intent); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index 4b4e71f6e..bb12cd7ff 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; + import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -43,14 +44,12 @@ import android.widget.Toast; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; @@ -75,8 +74,7 @@ public class PassphraseDialogActivity extends FragmentActivity { // special extra for OpenPgpService public static final String EXTRA_SERVICE_INTENT = "data"; - - private static final int REQUEST_CODE_ENTER_PATTERN = 2; + private long mSubKeyId; @Override protected void onCreate(Bundle savedInstanceState) { @@ -93,14 +91,13 @@ public class PassphraseDialogActivity extends FragmentActivity { // this activity itself has no content view (see manifest) - long keyId; if (getIntent().hasExtra(EXTRA_SUBKEY_ID)) { - keyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0); + mSubKeyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0); } else { RequiredInputParcel requiredInput = getIntent().getParcelableExtra(EXTRA_REQUIRED_INPUT); switch (requiredInput.mType) { case PASSPHRASE_SYMMETRIC: { - keyId = Constants.key.symmetric; + mSubKeyId = Constants.key.symmetric; break; } case PASSPHRASE: { @@ -127,7 +124,7 @@ public class PassphraseDialogActivity extends FragmentActivity { return; } - keyId = requiredInput.getSubKeyId(); + mSubKeyId = requiredInput.getSubKeyId(); break; } default: { @@ -136,62 +133,35 @@ public class PassphraseDialogActivity extends FragmentActivity { } } - Intent serviceIntent = getIntent().getParcelableExtra(EXTRA_SERVICE_INTENT); - - show(this, keyId, serviceIntent); } @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case REQUEST_CODE_ENTER_PATTERN: { - /* - * NOTE that there are 4 possible result codes!!! - */ - switch (resultCode) { - case RESULT_OK: - // The user passed - break; - case RESULT_CANCELED: - // The user cancelled the task - break; -// case LockPatternActivity.RESULT_FAILED: -// // The user failed to enter the pattern -// break; -// case LockPatternActivity.RESULT_FORGOT_PATTERN: -// // The user forgot the pattern and invoked your recovery Activity. -// break; - } + protected void onResume() { + super.onResume(); - /* - * In any case, there's always a key EXTRA_RETRY_COUNT, which holds - * the number of tries that the user did. - */ -// int retryCount = data.getIntExtra( -// LockPatternActivity.EXTRA_RETRY_COUNT, 0); + /* Show passphrase dialog to cache a new passphrase the user enters for using it later for + * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks + * for a symmetric passphrase + */ - break; - } - } + Intent serviceIntent = getIntent().getParcelableExtra(EXTRA_SERVICE_INTENT); + + PassphraseDialogFragment frag = new PassphraseDialogFragment(); + Bundle args = new Bundle(); + args.putLong(EXTRA_SUBKEY_ID, mSubKeyId); + args.putParcelable(EXTRA_SERVICE_INTENT, serviceIntent); + frag.setArguments(args); + frag.show(getSupportFragmentManager(), "passphraseDialog"); } - /** - * Shows passphrase dialog to cache a new passphrase the user enters for using it later for - * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks - * for a symmetric passphrase - */ - public static void show(final FragmentActivity context, final long keyId, final Intent serviceIntent) { - DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - public void run() { - // do NOT check if the key even needs a passphrase. that's not our job here. - PassphraseDialogFragment frag = new PassphraseDialogFragment(); - Bundle args = new Bundle(); - args.putLong(EXTRA_SUBKEY_ID, keyId); - args.putParcelable(EXTRA_SERVICE_INTENT, serviceIntent); - frag.setArguments(args); - frag.show(context.getSupportFragmentManager(), "passphraseDialog"); - } - }); + @Override + protected void onPause() { + super.onPause(); + + DialogFragment dialog = (DialogFragment) getSupportFragmentManager().findFragmentByTag("passphraseDialog"); + if (dialog != null) { + dialog.dismiss(); + } } public static class PassphraseDialogFragment extends DialogFragment implements TextView.OnEditorActionListener { @@ -205,9 +175,6 @@ public class PassphraseDialogActivity extends FragmentActivity { private Intent mServiceIntent; - /** - * Creates dialog - */ @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); @@ -268,12 +235,7 @@ public class PassphraseDialogActivity extends FragmentActivity { userId = null; } - /* Get key type for message */ - // find a master key id for our key - long masterKeyId = new ProviderHelper(activity).getMasterKeyId(mSubKeyId); - CachedPublicKeyRing keyRing = new ProviderHelper(activity).getCachedPublicKeyRing(masterKeyId); - // get the type of key (from the database) - keyType = keyRing.getSecretKeyType(mSubKeyId); + keyType = mSecretRing.getSecretKey(mSubKeyId).getSecretKeyType(); switch (keyType) { case PASSPHRASE: message = getString(R.string.passphrase_for, userId); @@ -468,20 +430,16 @@ public class PassphraseDialogActivity extends FragmentActivity { // note we need no synchronization here, this variable is only accessed in the ui thread mIsCancelled = true; + + getActivity().setResult(RESULT_CANCELED); + getActivity().finish(); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); - if (getActivity() == null) { - return; - } - hideKeyboard(); - - getActivity().setResult(RESULT_CANCELED); - getActivity().finish(); } private void hideKeyboard() { @@ -495,11 +453,9 @@ public class PassphraseDialogActivity extends FragmentActivity { inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } - /** - * Associate the "done" button on the soft keyboard with the okay button in the view - */ @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + // Associate the "done" button on the soft keyboard with the okay button in the view if (EditorInfo.IME_ACTION_DONE == actionId) { AlertDialog dialog = ((AlertDialog) getDialog()); Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index b92163b59..a929d52f0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -300,7 +300,7 @@ public class ViewKeyFragment extends LoaderFragment implements * because the notification triggers faster than the activity closes. */ // Avoid NullPointerExceptions... - if (data.getCount() == 0) { + if (data == null || data.getCount() == 0) { return; } // Swap the new cursor in. (The framework will take care of closing the diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java index deaaee87a..1fc24775b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java @@ -19,15 +19,16 @@ package org.sufficientlysecure.keychain.ui.adapter; import java.io.Serializable; -import java.util.Calendar; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Date; -import java.util.TimeZone; +import java.util.List; import android.content.Context; import android.database.Cursor; import android.graphics.PorterDuff; import android.support.v4.widget.CursorAdapter; -import android.text.format.DateFormat; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; @@ -60,7 +61,6 @@ public class KeyAdapter extends CursorAdapter { KeyRings.VERIFIED, KeyRings.HAS_ANY_SECRET, KeyRings.HAS_DUPLICATE_USER_ID, - KeyRings.HAS_ENCRYPT, KeyRings.FINGERPRINT, KeyRings.CREATION, }; @@ -72,9 +72,8 @@ public class KeyAdapter extends CursorAdapter { public static final int INDEX_VERIFIED = 5; public static final int INDEX_HAS_ANY_SECRET = 6; public static final int INDEX_HAS_DUPLICATE_USER_ID = 7; - public static final int INDEX_HAS_ENCRYPT = 8; - public static final int INDEX_FINGERPRINT = 9; - public static final int INDEX_CREATION = 10; + public static final int INDEX_FINGERPRINT = 8; + public static final int INDEX_CREATION = 9; public KeyAdapter(Context context, Cursor c, int flags) { super(context, c, flags); @@ -87,6 +86,7 @@ public class KeyAdapter extends CursorAdapter { } public static class KeyItemViewHolder { + public View mView; public Long mMasterKeyId; public TextView mMainUserId; public TextView mMainUserIdRest; @@ -96,6 +96,7 @@ public class KeyAdapter extends CursorAdapter { public ImageButton mSlingerButton; public KeyItemViewHolder(View view) { + mView = view; mMainUserId = (TextView) view.findViewById(R.id.key_list_item_name); mMainUserIdRest = (TextView) view.findViewById(R.id.key_list_item_email); mStatus = (ImageView) view.findViewById(R.id.key_list_item_status_icon); @@ -104,11 +105,10 @@ public class KeyAdapter extends CursorAdapter { mCreationDate = (TextView) view.findViewById(R.id.key_list_item_creation); } - public void setData(Context context, Cursor cursor, Highlighter highlighter) { + public void setData(Context context, KeyItem item, Highlighter highlighter) { { // set name and stuff, common to both key types - String userId = cursor.getString(INDEX_USER_ID); - KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); + KeyRing.UserId userIdSplit = item.mUserId; if (userIdSplit.name != null) { mMainUserId.setText(highlighter.highlight(userIdSplit.name)); } else { @@ -124,30 +124,23 @@ public class KeyAdapter extends CursorAdapter { { // set edit button and status, specific by key type - long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); - boolean isSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0; - boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0; - boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; - boolean hasDuplicate = cursor.getInt(INDEX_HAS_DUPLICATE_USER_ID) != 0; - - mMasterKeyId = masterKeyId; + mMasterKeyId = item.mKeyId; // Note: order is important! - if (isRevoked) { + if (item.mIsRevoked) { KeyFormattingUtils .setStatusImage(context, mStatus, null, State.REVOKED, R.color.bg_gray); mStatus.setVisibility(View.VISIBLE); mSlinger.setVisibility(View.GONE); mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray)); mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray)); - } else if (isExpired) { + } else if (item.mIsExpired) { KeyFormattingUtils.setStatusImage(context, mStatus, null, State.EXPIRED, R.color.bg_gray); mStatus.setVisibility(View.VISIBLE); mSlinger.setVisibility(View.GONE); mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray)); mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray)); - } else if (isSecret) { + } else if (item.mIsSecret) { mStatus.setVisibility(View.GONE); if (mSlingerButton.hasOnClickListeners()) { mSlinger.setVisibility(View.VISIBLE); @@ -158,7 +151,7 @@ public class KeyAdapter extends CursorAdapter { mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.black)); } else { // this is a public key - show if it's verified - if (isVerified) { + if (item.mIsVerified) { KeyFormattingUtils.setStatusImage(context, mStatus, State.VERIFIED); mStatus.setVisibility(View.VISIBLE); } else { @@ -170,9 +163,9 @@ public class KeyAdapter extends CursorAdapter { mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.black)); } - if (hasDuplicate) { + if (item.mHasDuplicate) { String dateTime = DateUtils.formatDateTime(context, - cursor.getLong(INDEX_CREATION) * 1000, + item.mCreation.getTime(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_MONTH); @@ -190,6 +183,10 @@ public class KeyAdapter extends CursorAdapter { } + public boolean isEnabled(Cursor cursor) { + return true; + } + @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { View view = mInflater.inflate(R.layout.key_list_item, parent, false); @@ -204,7 +201,8 @@ public class KeyAdapter extends CursorAdapter { public void bindView(View view, Context context, Cursor cursor) { Highlighter highlighter = new Highlighter(context, mQuery); KeyItemViewHolder h = (KeyItemViewHolder) view.getTag(); - h.setData(context, cursor, highlighter); + KeyItem item = new KeyItem(cursor); + h.setData(context, item, highlighter); } public boolean isSecretAvailable(int id) { @@ -234,8 +232,9 @@ public class KeyAdapter extends CursorAdapter { @Override public long getItemId(int position) { + Cursor cursor = getCursor(); // prevent a crash on rapid cursor changes - if (getCursor().isClosed()) { + if (cursor != null && getCursor().isClosed()) { return 0L; } return super.getItemId(position); @@ -250,6 +249,7 @@ public class KeyAdapter extends CursorAdapter { public final boolean mHasDuplicate; public final Date mCreation; public final String mFingerprint; + public final boolean mIsSecret, mIsRevoked, mIsExpired, mIsVerified; private KeyItem(Cursor cursor) { String userId = cursor.getString(INDEX_USER_ID); @@ -260,6 +260,10 @@ public class KeyAdapter extends CursorAdapter { mCreation = new Date(cursor.getLong(INDEX_CREATION) * 1000); mFingerprint = KeyFormattingUtils.convertFingerprintToHex( cursor.getBlob(INDEX_FINGERPRINT)); + mIsSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0; + mIsRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; + mIsExpired = cursor.getInt(INDEX_IS_EXPIRED) > 0; + mIsVerified = cursor.getInt(INDEX_VERIFIED) > 0; } public KeyItem(CanonicalizedPublicKeyRing ring) { @@ -272,6 +276,12 @@ public class KeyAdapter extends CursorAdapter { mCreation = key.getCreationTime(); mFingerprint = KeyFormattingUtils.convertFingerprintToHex( ring.getFingerprint()); + mIsRevoked = key.isRevoked(); + mIsExpired = key.isExpired(); + + // these two are actually "don't know"s + mIsSecret = false; + mIsVerified = false; } public String getReadableName() { @@ -284,4 +294,11 @@ public class KeyAdapter extends CursorAdapter { } + public static String[] getProjectionWith(String[] projection) { + List<String> list = new ArrayList<>(); + list.addAll(Arrays.asList(PROJECTION)); + list.addAll(Arrays.asList(projection)); + return list.toArray(new String[list.size()]); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java index 8c554dbde..7dfd56430 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java @@ -38,25 +38,16 @@ import org.sufficientlysecure.keychain.util.FabContainer; public class Notify { public static enum Style { - OK, WARN, ERROR; + OK (R.color.android_green_light), WARN(R.color.android_orange_light), ERROR(R.color.android_red_light); - public void applyToBar(Snackbar bar) { + public final int mLineColor; - switch (this) { - case OK: - // bar.actionColorResource(R.color.android_green_light); - bar.lineColorResource(R.color.android_green_light); - break; - case WARN: - // bar.textColorResource(R.color.android_orange_light); - bar.lineColorResource(R.color.android_orange_light); - break; - case ERROR: - // bar.textColorResource(R.color.android_red_light); - bar.lineColorResource(R.color.android_red_light); - break; - } + Style(int color) { + mLineColor = color; + } + public void applyToBar(Snackbar bar) { + bar.lineColorResource(mLineColor); } } 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 845b35512..0fed46544 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 @@ -17,6 +17,10 @@ package org.sufficientlysecure.keychain.ui.widget; + +import java.util.Arrays; +import java.util.List; + import android.content.Context; import android.database.Cursor; import android.net.Uri; @@ -24,14 +28,11 @@ import android.os.Bundle; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.util.AttributeSet; -import android.widget.ImageView; 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.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; +import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; public class CertifyKeySpinner extends KeySpinner { private long mHiddenMasterKeyId = Constants.key.none; @@ -59,19 +60,9 @@ public class CertifyKeySpinner extends KeySpinner { // 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_REVOKED, - KeychainContract.KeyRings.IS_EXPIRED, + String[] projection = KeyAdapter.getProjectionWith(new String[] { KeychainContract.KeyRings.HAS_CERTIFY, - KeychainContract.KeyRings.HAS_ANY_SECRET, - KeychainContract.KeyRings.HAS_DUPLICATE_USER_ID, - KeychainContract.KeyRings.CREATION - }; + }); String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeychainDatabase.Tables.KEYS + "." + KeychainContract.KeyRings.MASTER_KEY_ID @@ -82,7 +73,7 @@ public class CertifyKeySpinner extends KeySpinner { return new CursorLoader(getContext(), baseUri, projection, where, null, null); } - private int mIndexHasCertify, mIndexIsRevoked, mIndexIsExpired; + private int mIndexHasCertify; @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { @@ -90,8 +81,6 @@ public class CertifyKeySpinner extends KeySpinner { if (loader.getId() == LOADER_ID) { mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY); - mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED); - mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED); // If: // - no key has been pre-selected (e.g. by SageSlinger) @@ -119,18 +108,15 @@ public class CertifyKeySpinner extends KeySpinner { @Override - boolean setStatus(Context context, Cursor cursor, ImageView statusView) { - if (cursor.getInt(mIndexIsRevoked) != 0) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.REVOKED, R.color.bg_gray); + boolean isItemEnabled(Cursor cursor) { + if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) { return false; } - if (cursor.getInt(mIndexIsExpired) != 0) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.EXPIRED, R.color.bg_gray); + if (cursor.getInt(KeyAdapter.INDEX_IS_EXPIRED) != 0) { return false; } // don't invalidate the "None" entry, which is also null! if (cursor.getPosition() != 0 && cursor.isNull(mIndexHasCertify)) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.UNAVAILABLE, R.color.bg_gray); return false; } 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 63a1aade9..48e6c2cee 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 @@ -38,6 +38,7 @@ import com.tokenautocomplete.TokenCompleteTextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; @@ -126,7 +127,13 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView public Loader<Cursor> onCreateLoader(int id, Bundle args) { // These are the rows that we will retrieve. Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); - String where = KeyRings.HAS_ENCRYPT + " NOT NULL AND " + KeyRings.IS_EXPIRED + " = 0 AND " + + String[] projection = KeyAdapter.getProjectionWith(new String[] { + KeychainContract.KeyRings.HAS_ENCRYPT, + }); + + String where = KeyRings.HAS_ENCRYPT + " NOT NULL AND " + + KeyRings.IS_EXPIRED + " = 0 AND " + Tables.KEYS + "." + KeyRings.IS_REVOKED + " = 0"; if (args != null && args.containsKey(ARG_QUERY)) { @@ -135,12 +142,12 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView where += " AND " + KeyRings.USER_ID + " LIKE ?"; - return new CursorLoader(getContext(), baseUri, KeyAdapter.PROJECTION, where, + return new CursorLoader(getContext(), baseUri, projection, where, new String[]{"%" + query + "%"}, null); } mAdapter.setSearchQuery(null); - return new CursorLoader(getContext(), baseUri, KeyAdapter.PROJECTION, where, null, null); + return new CursorLoader(getContext(), baseUri, projection, where, null, null); } 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 ad1a14a33..5050c01af 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 @@ -17,33 +17,30 @@ package org.sufficientlysecure.keychain.ui.widget; -import android.annotation.SuppressLint; + import android.content.Context; import android.database.Cursor; -import android.graphics.Color; import android.os.Bundle; import android.os.Parcelable; import android.support.annotation.NonNull; 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.support.v7.widget.AppCompatSpinner; -import android.text.format.DateUtils; 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.ImageView; 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.provider.KeychainContract; -import org.sufficientlysecure.keychain.util.Log; +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. @@ -119,7 +116,8 @@ public abstract class KeySpinner extends AppCompatSpinner implements if (getContext() instanceof FragmentActivity) { ((FragmentActivity) getContext()).getSupportLoaderManager().restartLoader(LOADER_ID, null, this); } else { - Log.e(Constants.TAG, "KeySpinner must be attached to FragmentActivity, this is " + getContext().getClass()); + throw new AssertionError("KeySpinner must be attached to FragmentActivity, this is " + + getContext().getClass()); } } @@ -138,7 +136,11 @@ public abstract class KeySpinner extends AppCompatSpinner implements } public long getSelectedKeyId() { - return getSelectedItemId(); + Object item = getSelectedItem(); + if (item instanceof KeyItem) { + return ((KeyItem) item).mKeyId; + } + return Constants.key.none; } public void setPreSelectedKeyId(long selectedKeyId) { @@ -146,161 +148,87 @@ public abstract class KeySpinner extends AppCompatSpinner implements } protected class SelectKeyAdapter extends BaseAdapter implements SpinnerAdapter { - private CursorAdapter inner; - private int mIndexUserId; - private int mIndexDuplicate; + private KeyAdapter inner; private int mIndexMasterKeyId; - private int mIndexCreationDate; public SelectKeyAdapter() { - inner = new CursorAdapter(getContext(), null, 0) { - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return View.inflate(getContext(), R.layout.keyspinner_item, null); - } + inner = new KeyAdapter(getContext(), null, 0) { @Override - 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 vDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate); - - KeyRing.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexUserId)); - vKeyName.setText(userId.name); - vKeyEmail.setText(userId.email); - - boolean duplicate = cursor.getLong(mIndexDuplicate) > 0; - if (duplicate) { - String dateTime = DateUtils.formatDateTime(context, - cursor.getLong(mIndexCreationDate) * 1000, - DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_SHOW_YEAR - | DateUtils.FORMAT_ABBREV_MONTH); - - vDuplicate.setText(context.getString(R.string.label_key_created, dateTime)); - vDuplicate.setVisibility(View.VISIBLE); - } else { - vDuplicate.setVisibility(View.GONE); - } - - boolean valid = setStatus(getContext(), cursor, vKeyStatus); - setItemEnabled(view, valid); + public boolean isEnabled(Cursor cursor) { + return KeySpinner.this.isItemEnabled(cursor); } - @Override - public long getItemId(int position) { - try { - return ((Cursor) getItem(position)).getLong(mIndexMasterKeyId); - } catch (Exception e) { - // This can happen on concurrent modification :( - return Constants.key.none; - } - } }; } - private void setItemEnabled(View view, boolean enabled) { - 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 vKeyDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate); - - if (enabled) { - vKeyName.setTextColor(Color.BLACK); - vKeyEmail.setTextColor(Color.BLACK); - vKeyDuplicate.setTextColor(Color.BLACK); - vKeyStatus.setVisibility(View.GONE); - view.setClickable(false); - } else { - vKeyName.setTextColor(Color.GRAY); - vKeyEmail.setTextColor(Color.GRAY); - vKeyDuplicate.setTextColor(Color.GRAY); - vKeyStatus.setVisibility(View.VISIBLE); - // this is a HACK. the trick is, if the element itself is clickable, the - // click is not passed on to the view list - view.setClickable(true); - } - } - public Cursor swapCursor(Cursor newCursor) { if (newCursor == null) return inner.swapCursor(null); - mIndexDuplicate = newCursor.getColumnIndex(KeychainContract.KeyRings.HAS_DUPLICATE_USER_ID); - mIndexUserId = newCursor.getColumnIndex(KeychainContract.KeyRings.USER_ID); mIndexMasterKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID); - mIndexCreationDate = newCursor.getColumnIndex(KeychainContract.KeyRings.CREATION); + + 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); + setSelection(newCursor.getPosition() +1); } } while (newCursor.moveToNext()); } - return inner.swapCursor(newCursor); + return oldCursor; } @Override public int getCount() { - return inner.getCount() + 1; + return inner.getCount() +1; } @Override public Object getItem(int position) { - if (position == 0) return null; - return inner.getItem(position - 1); + 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); + if (position == 0) { + return Constants.key.none; + } + return inner.getItemId(position -1); } - @SuppressLint("ViewHolder") // inflate call is for the preview only @Override public View getView(int position, View convertView, ViewGroup parent) { - try { - View v = getDropDownView(position, convertView, parent); - v.findViewById(R.id.keyspinner_key_email).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 view; - if (position == 0) { - if (convertView == null) { - view = inner.newView(null, null, parent); - } else { - view = convertView; + // 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; } - 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 vKeyDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate); - - vKeyName.setText(R.string.choice_none); - vKeyEmail.setVisibility(View.GONE); - vKeyDuplicate.setVisibility(View.GONE); - vKeyStatus.setVisibility(View.GONE); - setItemEnabled(view, true); - } else { - view = inner.getView(position - 1, convertView, parent); - TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email); - vKeyEmail.setVisibility(View.VISIBLE); } - return view; + + if (position > 0) { + return inner.getView(position -1, convertView, parent); + } + + return convertView != null ? convertView : + LayoutInflater.from(getContext()).inflate( + R.layout.keyspinner_item_none, parent, false); } + } - boolean setStatus(Context context, Cursor cursor, ImageView statusView) { + boolean isItemEnabled(Cursor cursor) { return true; } 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 index df7347fa4..c59ad7a12 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui.widget; + import android.content.Context; import android.database.Cursor; import android.net.Uri; @@ -24,12 +25,9 @@ import android.os.Bundle; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.util.AttributeSet; -import android.widget.ImageView; -import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; +import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; public class SignKeySpinner extends KeySpinner { public SignKeySpinner(Context context) { @@ -50,19 +48,9 @@ public class SignKeySpinner extends KeySpinner { // 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_REVOKED, - KeychainContract.KeyRings.IS_EXPIRED, + String[] projection = KeyAdapter.getProjectionWith(new String[] { KeychainContract.KeyRings.HAS_SIGN, - KeychainContract.KeyRings.HAS_ANY_SECRET, - KeychainContract.KeyRings.HAS_DUPLICATE_USER_ID, - KeychainContract.KeyRings.CREATION - }; + }); String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1"; @@ -71,7 +59,7 @@ public class SignKeySpinner extends KeySpinner { return new CursorLoader(getContext(), baseUri, projection, where, null, null); } - private int mIndexHasSign, mIndexIsRevoked, mIndexIsExpired; + private int mIndexHasSign; @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { @@ -79,23 +67,18 @@ public class SignKeySpinner extends KeySpinner { if (loader.getId() == LOADER_ID) { mIndexHasSign = data.getColumnIndex(KeychainContract.KeyRings.HAS_SIGN); - 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, null, State.REVOKED, R.color.bg_gray); + boolean isItemEnabled(Cursor cursor) { + if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) { return false; } - if (cursor.getInt(mIndexIsExpired) != 0) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.EXPIRED, R.color.bg_gray); + if (cursor.getInt(KeyAdapter.INDEX_IS_EXPIRED) != 0) { return false; } if (cursor.getInt(mIndexHasSign) == 0) { - KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.UNAVAILABLE, R.color.bg_gray); return false; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ToolableViewAnimator.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ToolableViewAnimator.java new file mode 100644 index 000000000..44f2b7c3e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ToolableViewAnimator.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse> + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.sufficientlysecure.keychain.ui.widget; + + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ViewAnimator; + +import org.sufficientlysecure.keychain.R; + + +/** This view is essentially identical to ViewAnimator, but allows specifying the initial view + * for preview as an xml attribute. */ +public class ToolableViewAnimator extends ViewAnimator { + + private int mInitChild = -1; + + public ToolableViewAnimator(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs); + + if (isInEditMode()) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ToolableViewAnimator, defStyleAttr, 0); + mInitChild = a.getInt(R.styleable.ToolableViewAnimator_initialView, -1); + a.recycle(); + } + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (isInEditMode() && mInitChild-- > 0) { + return; + } + super.addView(child, index, params); + } +} diff --git a/OpenKeychain/src/main/res/layout/create_key_passphrase_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_passphrase_fragment.xml index 48b86765a..abfb2861b 100644 --- a/OpenKeychain/src/main/res/layout/create_key_passphrase_fragment.xml +++ b/OpenKeychain/src/main/res/layout/create_key_passphrase_fragment.xml @@ -34,7 +34,8 @@ android:inputType="textPassword" android:hint="@string/label_passphrase" android:ems="10" - android:layout_gravity="center_horizontal" /> + android:layout_gravity="center_horizontal" + /> <EditText android:id="@+id/create_key_passphrase_again" diff --git a/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml index ea6be462f..cb20254ff 100644 --- a/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml +++ b/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml @@ -1,11 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:visibility="gone" + tools:visibility="visible" android:id="@+id/decrypt_content" android:layout_width="match_parent" android:layout_height="match_parent" @@ -28,6 +30,7 @@ android:layout_height="wrap_content" android:gravity="top" android:hint="" + tools:text="This is the plaintext" android:textIsSelectable="true" /> </ScrollView> diff --git a/OpenKeychain/src/main/res/layout/keyspinner_item.xml b/OpenKeychain/src/main/res/layout/keyspinner_item.xml deleted file mode 100644 index eea81eba5..000000000 --- a/OpenKeychain/src/main/res/layout/keyspinner_item.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:singleLine="true" - android:orientation="horizontal" - android:descendantFocusability="blocksDescendants" - android:focusable="false" - android:minHeight="44dip"> - - <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"> - - <TextView - android:id="@+id/keyspinner_key_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/label_main_user_id" - android:textAppearance="?android:attr/textAppearanceMedium" /> - - <TextView - android:id="@+id/keyspinner_key_email" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="end" - android:text="user@example.com" - android:textAppearance="?android:attr/textAppearanceSmall" /> - - <TextView - android:id="@+id/keyspinner_duplicate" - android:text="creation: bla" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="end" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </LinearLayout> - - <ImageView - android:id="@+id/keyspinner_key_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:src="@drawable/status_signature_revoked_cutout_24dp" - android:paddingLeft="16dp" - android:paddingRight="16dp" /> - -</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/keyspinner_item_none.xml b/OpenKeychain/src/main/res/layout/keyspinner_item_none.xml new file mode 100644 index 000000000..bd1d4ac11 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/keyspinner_item_none.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:singleLine="true" + android:orientation="horizontal" + android:minHeight="44dip" + android:paddingLeft="8dp" + android:paddingRight="4dp"> + + <TextView + android:id="@+id/keyspinner_key_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/choice_none" + android:textAppearance="?android:attr/textAppearanceMedium" /> + +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/view_key_activity.xml b/OpenKeychain/src/main/res/layout/view_key_activity.xml index 4dbd793ea..b3f4e721d 100644 --- a/OpenKeychain/src/main/res/layout/view_key_activity.xml +++ b/OpenKeychain/src/main/res/layout/view_key_activity.xml @@ -60,6 +60,7 @@ android:layout_marginRight="48dp" android:layout_marginEnd="48dp" android:text="" + tools:text="Alice Skywalker" android:textColor="@color/icons" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_above="@+id/view_key_status" /> @@ -73,6 +74,7 @@ android:layout_marginRight="48dp" android:layout_marginEnd="48dp" android:text="" + tools:text="My Key" android:textColor="@color/tab_text" android:textAppearance="?android:attr/textAppearanceSmall" android:layout_above="@+id/toolbar2" /> @@ -95,6 +97,7 @@ android:layout_width="64dp" android:layout_height="64dp" android:visibility="invisible" + tools:visibility="visible" style="?android:attr/borderlessButtonStyle" android:src="@drawable/ic_action_encrypt_file_24dp" /> @@ -103,6 +106,7 @@ android:layout_width="64dp" android:layout_height="64dp" android:visibility="invisible" + tools:visibility="visible" style="?android:attr/borderlessButtonStyle" android:src="@drawable/ic_action_encrypt_text_24dp" /> @@ -111,6 +115,7 @@ android:layout_width="64dp" android:layout_height="64dp" android:visibility="invisible" + tools:visibility="visible" style="?android:attr/borderlessButtonStyle" android:src="@drawable/ic_nfc_white_24dp" /> @@ -120,6 +125,7 @@ android:id="@+id/view_key_status_image" android:layout_width="96dp" android:visibility="invisible" + tools:visibility="visible" android:src="@drawable/status_signature_unverified_cutout_96dp" android:layout_height="96dp" android:layout_above="@id/toolbar2" @@ -139,6 +145,7 @@ android:layout_height="wrap_content" android:clickable="true" android:foreground="?android:attr/selectableItemBackground" + tools:visibility="invisible" card_view:cardBackgroundColor="@android:color/white" card_view:cardElevation="2dp" card_view:cardUseCompatPadding="true" @@ -147,7 +154,8 @@ <ImageView android:id="@+id/view_key_qr_code" android:layout_width="96dp" - android:layout_height="96dp" /> + android:layout_height="96dp" + /> </android.support.v7.widget.CardView> </RelativeLayout> @@ -189,6 +197,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="invisible" + tools:visibility="visible" android:elevation="4dp" fab:fab_icon="@drawable/ic_qrcode_white_24dp" fab:fab_colorNormal="@color/fab" diff --git a/OpenKeychain/src/main/res/values/attr.xml b/OpenKeychain/src/main/res/values/attr.xml index 7a2f3054e..04642cabb 100644 --- a/OpenKeychain/src/main/res/values/attr.xml +++ b/OpenKeychain/src/main/res/values/attr.xml @@ -1,6 +1,10 @@ <?xml version="1.0" encoding="utf-8"?> <resources> + <declare-styleable name="ToolableViewAnimator"> + <attr name="initialView" format="integer" /> + </declare-styleable> + <declare-styleable name="FoldableLinearLayout"> <attr name="foldedLabel" format="string" /> <attr name="unFoldedLabel" format="string" /> |