diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java')
-rw-r--r-- | OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java | 779 |
1 files changed, 403 insertions, 376 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 7ae4f1be3..fd50ed5ef 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -31,9 +31,10 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.os.Message; -import android.os.Messenger; import android.provider.ContactsContract; +import android.support.design.widget.AppBarLayout; +import android.support.design.widget.CollapsingToolbarLayout; +import android.support.design.widget.FloatingActionButton; import android.support.v4.app.ActivityCompat; import android.support.v4.app.FragmentManager; import android.support.v4.app.LoaderManager; @@ -47,16 +48,17 @@ import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; -import com.getbase.floatingactionbutton.FloatingActionButton; + + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; -import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.KeyRing; @@ -65,13 +67,10 @@ import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard; -import org.sufficientlysecure.keychain.service.ServiceProgressHandler; -import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; -import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; @@ -85,37 +84,45 @@ import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.NfcHelper; import org.sufficientlysecure.keychain.util.Preferences; +import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; public class ViewKeyActivity extends BaseNfcActivity implements - LoaderManager.LoaderCallbacks<Cursor> { + LoaderManager.LoaderCallbacks<Cursor>, + CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> { public static final String EXTRA_NFC_USER_ID = "nfc_user_id"; public static final String EXTRA_NFC_AID = "nfc_aid"; public static final String EXTRA_NFC_FINGERPRINTS = "nfc_fingerprints"; static final int REQUEST_QR_FINGERPRINT = 1; - static final int REQUEST_DELETE = 2; - static final int REQUEST_EXPORT = 3; + static final int REQUEST_BACKUP = 2; + static final int REQUEST_CERTIFY = 3; + static final int REQUEST_DELETE = 4; + public static final String EXTRA_DISPLAY_RESULT = "display_result"; - ExportHelper mExportHelper; ProviderHelper mProviderHelper; protected Uri mDataUri; - private TextView mName; + // For CryptoOperationHelper.Callback + private String mKeyserver; + private ArrayList<ParcelableKeyRing> mKeyList; + private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper; + private TextView mStatusText; private ImageView mStatusImage; - private RelativeLayout mBigToolbar; + private AppBarLayout mAppBarLayout; + private CollapsingToolbarLayout mCollapsingToolbarLayout; private ImageButton mActionEncryptFile; private ImageButton mActionEncryptText; private ImageButton mActionNfc; private FloatingActionButton mFab; private ImageView mPhoto; + private FrameLayout mPhotoLayout; private ImageView mQrCode; private CardView mQrCodeLayout; @@ -132,6 +139,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements private boolean mIsRevoked = false; private boolean mIsExpired = false; + private boolean mShowYubikeyAfterCreation = false; + private MenuItem mRefreshItem; private boolean mIsRefreshing; private Animation mRotate, mRotateSpin; @@ -139,26 +148,31 @@ public class ViewKeyActivity extends BaseNfcActivity implements private String mFingerprint; private long mMasterKeyId; + private byte[] mNfcFingerprints; + private String mNfcUserId; + private byte[] mNfcAid; + @SuppressLint("InflateParams") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mExportHelper = new ExportHelper(this); mProviderHelper = new ProviderHelper(this); + mOperationHelper = new CryptoOperationHelper<>(1, this, this, null); setTitle(null); - mName = (TextView) findViewById(R.id.view_key_name); mStatusText = (TextView) findViewById(R.id.view_key_status); mStatusImage = (ImageView) findViewById(R.id.view_key_status_image); - mBigToolbar = (RelativeLayout) findViewById(R.id.toolbar_big); + mAppBarLayout = (AppBarLayout) findViewById(R.id.app_bar_layout); + mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar); mActionEncryptFile = (ImageButton) findViewById(R.id.view_key_action_encrypt_files); mActionEncryptText = (ImageButton) findViewById(R.id.view_key_action_encrypt_text); mActionNfc = (ImageButton) findViewById(R.id.view_key_action_nfc); mFab = (FloatingActionButton) findViewById(R.id.fab); mPhoto = (ImageView) findViewById(R.id.view_key_photo); + mPhotoLayout = (FrameLayout) findViewById(R.id.view_key_photo_layout); mQrCode = (ImageView) findViewById(R.id.view_key_qr_code); mQrCodeLayout = (CardView) findViewById(R.id.view_key_qr_code_layout); @@ -287,14 +301,17 @@ public class ViewKeyActivity extends BaseNfcActivity implements .replace(R.id.view_key_fragment, frag) .commit(); - if (getIntent().hasExtra(EXTRA_NFC_AID)) { - Intent intent = getIntent(); - byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS); - String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID); - byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID); - showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); + if (Preferences.getPreferences(this).getExperimentalEnableKeybase()) { + final ViewKeyKeybaseFragment keybaseFrag = ViewKeyKeybaseFragment.newInstance(mDataUri); + manager.beginTransaction() + .replace(R.id.view_key_keybase_fragment, keybaseFrag) + .commit(); } + // need to postpone loading of the yubikey fragment until after mMasterKeyId + // is available, but we mark here that this should be done + mShowYubikeyAfterCreation = true; + } @Override @@ -320,31 +337,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements return true; } case R.id.menu_key_view_export_file: { - try { - if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) { - exportToFile(mDataUri, mExportHelper, mProviderHelper); - return true; - } - - startPassphraseActivity(REQUEST_EXPORT); - } catch (PassphraseCacheService.KeyNotFoundException e) { - // This happens when the master key is stripped - exportToFile(mDataUri, mExportHelper, mProviderHelper); - } + startPassphraseActivity(REQUEST_BACKUP); return true; } case R.id.menu_key_view_delete: { - try { - if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) { - deleteKey(); - return true; - } - - startPassphraseActivity(REQUEST_DELETE); - } catch (PassphraseCacheService.KeyNotFoundException e) { - // This happens when the master key is stripped - deleteKey(); - } + deleteKey(); return true; } case R.id.menu_key_view_advanced: { @@ -372,7 +369,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements return true; } case R.id.menu_key_view_certify_fingerprint: { - certifyFingeprint(mDataUri); + certifyFingeprint(mDataUri, false); + return true; + } + case R.id.menu_key_view_certify_fingerprint_word: { + certifyFingeprint(mDataUri, true); return true; } } @@ -384,11 +385,17 @@ public class ViewKeyActivity extends BaseNfcActivity implements MenuItem editKey = menu.findItem(R.id.menu_key_view_edit); editKey.setVisible(mIsSecret); + MenuItem exportKey = menu.findItem(R.id.menu_key_view_export_file); + exportKey.setVisible(mIsSecret); + MenuItem addLinked = menu.findItem(R.id.menu_key_view_add_linked_identity); addLinked.setVisible(mIsSecret); MenuItem certifyFingerprint = menu.findItem(R.id.menu_key_view_certify_fingerprint); certifyFingerprint.setVisible(!mIsSecret && !mIsVerified && !mIsExpired && !mIsRevoked); + MenuItem certifyFingerprintWord = menu.findItem(R.id.menu_key_view_certify_fingerprint_word); + certifyFingerprintWord.setVisible(!mIsSecret && !mIsVerified && !mIsExpired && !mIsRevoked + && Preferences.getPreferences(this).getExperimentalEnableWordConfirm()); return true; } @@ -400,40 +407,19 @@ public class ViewKeyActivity extends BaseNfcActivity implements startActivityForResult(scanQrCode, REQUEST_QR_FINGERPRINT); } - private void certifyFingeprint(Uri dataUri) { + private void certifyFingeprint(Uri dataUri, boolean enableWordConfirm) { Intent intent = new Intent(this, CertifyFingerprintActivity.class); intent.setData(dataUri); + intent.putExtra(CertifyFingerprintActivity.EXTRA_ENABLE_WORD_CONFIRM, enableWordConfirm); - startCertifyIntent(intent); + startActivityForResult(intent, REQUEST_CERTIFY); } private void certifyImmediate() { Intent intent = new Intent(this, CertifyKeyActivity.class); - intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] {mMasterKeyId}); + intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId}); - startCertifyIntent(intent); - } - - private void startCertifyIntent(Intent intent) { - // Message is received after signing is done in KeychainIntentService - ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == MessageStatus.OKAY.ordinal()) { - Bundle data = message.getData(); - CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); - - result.createNotify(ViewKeyActivity.this).show(); - } - } - }; - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - startActivityForResult(intent, 0); + startActivityForResult(intent, REQUEST_CERTIFY); } private void showQrCodeDialog() { @@ -458,95 +444,108 @@ public class ViewKeyActivity extends BaseNfcActivity implements startActivityForResult(intent, requestCode); } - private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) { - try { - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri); - - HashMap<String, Object> data = providerHelper.getGenericData( - baseUri, - new String[] {KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET}, - new int[] {ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER}); - - exportHelper.showExportKeysDialog( - new long[] {(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)}, - Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0) - ); - } catch (ProviderHelper.NotFoundException e) { - Notify.create(this, R.string.error_key_not_found, Notify.Style.ERROR).show(); - Log.e(Constants.TAG, "Key not found", e); - } + private void backupToFile() { + new ExportHelper(this).showExportKeysDialog( + mMasterKeyId, new File(Constants.Path.APP_DIR, + KeyFormattingUtils.convertKeyIdToHex(mMasterKeyId) + ".sec.asc"), true); } private void deleteKey() { - // Message is received after key is deleted - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.arg1 == MessageStatus.OKAY.ordinal()) { - setResult(RESULT_CANCELED); - finish(); - } - } - }; + Intent deleteIntent = new Intent(this, DeleteKeyDialogActivity.class); + + deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS, + new long[]{mMasterKeyId}); + deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, mIsSecret); + if (mIsSecret) { + // for upload in case key is secret + deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_KEYSERVER, + Preferences.getPreferences(this).getPreferredKeyserver()); + } - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - new long[] {mMasterKeyId}); - deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog"); + startActivityForResult(deleteIntent, REQUEST_DELETE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_QR_FINGERPRINT && resultCode == Activity.RESULT_OK) { + if (mOperationHelper.handleActivityResult(requestCode, resultCode, data)) { + return; + } - // If there is an EXTRA_RESULT, that's an error. Just show it. - if (data.hasExtra(OperationResult.EXTRA_RESULT)) { - OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); - result.createNotify(this).show(); + switch (requestCode) { + case REQUEST_QR_FINGERPRINT: { + + if (resultCode != Activity.RESULT_OK) { + return; + } + + // If there is an EXTRA_RESULT, that's an error. Just show it. + if (data.hasExtra(OperationResult.EXTRA_RESULT)) { + OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); + result.createNotify(this).show(); + return; + } + + String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT); + if (fp == null) { + Notify.create(this, R.string.error_scan_fp, Notify.LENGTH_LONG, Style.ERROR).show(); + return; + } + if (mFingerprint.equalsIgnoreCase(fp)) { + certifyImmediate(); + } else { + Notify.create(this, R.string.error_scan_match, Notify.LENGTH_LONG, Style.ERROR).show(); + } return; } - String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT); - if (fp == null) { - Notify.create(this, "Error scanning fingerprint!", - Notify.LENGTH_LONG, Notify.Style.ERROR).show(); + case REQUEST_BACKUP: { + if (resultCode != Activity.RESULT_OK) { + return; + } + + backupToFile(); return; } - if (mFingerprint.equalsIgnoreCase(fp)) { - certifyImmediate(); - } else { - Notify.create(this, "Fingerprints did not match!", - Notify.LENGTH_LONG, Notify.Style.ERROR).show(); + + case REQUEST_CERTIFY: { + if (resultCode != Activity.RESULT_OK) { + return; + } + + if (data.hasExtra(OperationResult.EXTRA_RESULT)) { + OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); + result.createNotify(this).show(); + } + return; } - return; - } + case REQUEST_DELETE: { + if (resultCode != Activity.RESULT_OK) { + return; + } - if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK) { - deleteKey(); + setResult(RESULT_OK, data); + finish(); + return; + } } - if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK) { - exportToFile(mDataUri, mExportHelper, mProviderHelper); - } + super.onActivityResult(requestCode, resultCode, data); - if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { - OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); - result.createNotify(this).show(); - } else { - super.onActivityResult(requestCode, resultCode, data); - } } @Override - protected void onNfcPerform() throws IOException { + protected void doNfcInBackground() throws IOException { - final byte[] nfcFingerprints = nfcGetFingerprints(); - final String nfcUserId = nfcGetUserId(); - final byte[] nfcAid = nfcGetAid(); + mNfcFingerprints = nfcGetFingerprints(); + mNfcUserId = nfcGetUserId(); + mNfcAid = nfcGetAid(); + } + + @Override + protected void onNfcPostExecute() throws IOException { - long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); + long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints); try { @@ -557,7 +556,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements // if the master key of that key matches this one, just show the yubikey dialog if (KeyFormattingUtils.convertFingerprintToHex(candidateFp).equals(mFingerprint)) { - showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); + showYubiKeyFragment(mNfcFingerprints, mNfcUserId, mNfcAid); return; } @@ -570,14 +569,13 @@ public class ViewKeyActivity extends BaseNfcActivity implements Intent intent = new Intent( ViewKeyActivity.this, ViewKeyActivity.class); intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints); startActivity(intent); finish(); } }, R.string.snack_yubikey_view).show(); - // and if it's not found, offer import } catch (PgpKeyNotFoundException e) { Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG, @@ -586,28 +584,36 @@ public class ViewKeyActivity extends BaseNfcActivity implements public void onAction() { Intent intent = new Intent( ViewKeyActivity.this, CreateKeyActivity.class); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints); startActivity(intent); finish(); } }, R.string.snack_yubikey_import).show(); } - } - public void showYubiKeyFragment(byte[] nfcFingerprints, String nfcUserId, byte[] nfcAid) { - ViewKeyYubiKeyFragment frag = ViewKeyYubiKeyFragment.newInstance( - nfcFingerprints, nfcUserId, nfcAid); + public void showYubiKeyFragment( + final byte[] nfcFingerprints, final String nfcUserId, final byte[] nfcAid) { - FragmentManager manager = getSupportFragmentManager(); + new Handler().post(new Runnable() { + @Override + public void run() { + ViewKeyYubiKeyFragment frag = ViewKeyYubiKeyFragment.newInstance( + mMasterKeyId, nfcFingerprints, nfcUserId, nfcAid); + + FragmentManager manager = getSupportFragmentManager(); + + manager.popBackStack("yubikey", FragmentManager.POP_BACK_STACK_INCLUSIVE); + manager.beginTransaction() + .addToBackStack("yubikey") + .replace(R.id.view_key_fragment, frag) + // if this is called while the activity wasn't resumed, just forget it happened + .commitAllowingStateLoss(); + } + }); - manager.popBackStack("yubikey", FragmentManager.POP_BACK_STACK_INCLUSIVE); - manager.beginTransaction() - .addToBackStack("yubikey") - .replace(R.id.view_key_fragment, frag) - .commit(); } private void encrypt(Uri dataUri, boolean text) { @@ -620,7 +626,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements long keyId = new ProviderHelper(this) .getCachedPublicKeyRing(dataUri) .extractOrGetMasterKeyId(); - long[] encryptionKeyIds = new long[] {keyId}; + long[] encryptionKeyIds = new long[]{keyId}; Intent intent; if (text) { intent = new Intent(this, EncryptTextActivity.class); @@ -638,76 +644,6 @@ public class ViewKeyActivity extends BaseNfcActivity implements } } - private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper) - throws ProviderHelper.NotFoundException { - - mIsRefreshing = true; - mRefreshItem.setEnabled(false); - mRefreshItem.setActionView(mRefresh); - mRefresh.startAnimation(mRotate); - - byte[] blob = (byte[]) providerHelper.getGenericData( - KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri), - KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB); - String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob); - - ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null); - ArrayList<ParcelableKeyRing> entries = new ArrayList<>(); - entries.add(keyEntry); - - // Message is received after importing is done in KeychainIntentService - ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == MessageStatus.OKAY.ordinal()) { - // get returned data bundle - Bundle returnData = message.getData(); - - mIsRefreshing = false; - - if (returnData == null) { - finish(); - return; - } - final ImportKeyResult result = - returnData.getParcelable(OperationResult.EXTRA_RESULT); - result.createNotify(ViewKeyActivity.this).show(); - } - } - }; - - // fill values for this action - Bundle data = new Bundle(); - - // search config - { - Preferences prefs = Preferences.getPreferences(this); - Preferences.CloudSearchPrefs cloudPrefs = - new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); - data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver); - } - - data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, entries); - - // Send all information needed to service to query keys in other thread - Intent intent = new Intent(this, KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING); - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(serviceHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - serviceHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - - } - private void editKey(Uri dataUri) { Intent editIntent = new Intent(this, EditKeyActivity.class); editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); @@ -735,9 +671,12 @@ public class ViewKeyActivity extends BaseNfcActivity implements AsyncTask<Void, Void, Bitmap> loadTask = new AsyncTask<Void, Void, Bitmap>() { protected Bitmap doInBackground(Void... unused) { - String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; + Uri uri = new Uri.Builder() + .scheme(Constants.FINGERPRINT_SCHEME) + .opaquePart(fingerprint) + .build(); // render with minimal size - return QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0); + return QrCodeUtils.getQRCodeBitmap(uri, 0); } protected void onPostExecute(Bitmap qrCode) { @@ -761,7 +700,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[] { + static final String[] PROJECTION = new String[]{ KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID, KeychainContract.KeyRings.USER_ID, @@ -797,6 +736,25 @@ public class ViewKeyActivity extends BaseNfcActivity implements int mPreviousColor = 0; + /** + * Calculate a reasonable color for the status bar based on the given toolbar color. + * Style guides want the toolbar color to be a "700" on the Android scale and the status + * bar should be the same color at "500", this is roughly 17 / 20th of the value in each + * channel. + * http://www.google.com/design/spec/style/color.html#color-color-palette + */ + static public int getStatusBarBackgroundColor(int color) { + int r = (color >> 16) & 0xff; + int g = (color >> 8) & 0xff; + int b = color & 0xff; + + r = r * 17 / 20; + g = g * 17 / 20; + b = b * 17 / 20; + + return (0xff << 24) | (r << 16) | (g << 8) | b; + } + @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { /* TODO better error handling? May cause problems when a key is deleted, @@ -809,172 +767,181 @@ public class ViewKeyActivity extends BaseNfcActivity implements // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) switch (loader.getId()) { - case LOADER_ID_UNIFIED: { - if (!data.moveToFirst()) { - return; - } - - // get name, email, and comment from USER_ID - KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); - if (mainUserId.name != null) { - mName.setText(mainUserId.name); - } else { - mName.setText(R.string.user_id_no_name); - } - - mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); - mFingerprint = KeyFormattingUtils.convertFingerprintToHex( - data.getBlob(INDEX_FINGERPRINT)); - - mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; - mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; - mIsRevoked = data.getInt(INDEX_IS_REVOKED) > 0; - mIsExpired = data.getInt(INDEX_IS_EXPIRED) != 0; - mIsVerified = data.getInt(INDEX_VERIFIED) > 0; - - // if the refresh animation isn't playing - if (!mRotate.hasStarted() && !mRotateSpin.hasStarted()) { - // re-create options menu based on mIsSecret, mIsVerified - supportInvalidateOptionsMenu(); - // this is done at the end of the animation otherwise - } - - AsyncTask<Long, Void, Bitmap> photoTask = - new AsyncTask<Long, Void, Bitmap>() { - protected Bitmap doInBackground(Long... mMasterKeyId) { - return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(), mMasterKeyId[0], true); - } - - protected void onPostExecute(Bitmap photo) { - mPhoto.setImageBitmap(photo); - mPhoto.setVisibility(View.VISIBLE); - } - }; - - // Note: order is important - int color; - if (mIsRevoked) { - mStatusText.setText(R.string.view_key_revoked); - mStatusImage.setVisibility(View.VISIBLE); - KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, - State.REVOKED, R.color.icons, true); - color = getResources().getColor(R.color.android_red_light); - - mActionEncryptFile.setVisibility(View.GONE); - mActionEncryptText.setVisibility(View.GONE); - mActionNfc.setVisibility(View.GONE); - mFab.setVisibility(View.GONE); - mQrCodeLayout.setVisibility(View.GONE); - } else if (mIsExpired) { - if (mIsSecret) { - mStatusText.setText(R.string.view_key_expired_secret); + if (data.moveToFirst()) { + // get name, email, and comment from USER_ID + KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); + if (mainUserId.name != null) { + mCollapsingToolbarLayout.setTitle(mainUserId.name); } else { - mStatusText.setText(R.string.view_key_expired); - } - mStatusImage.setVisibility(View.VISIBLE); - KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, - State.EXPIRED, R.color.icons, true); - color = getResources().getColor(R.color.android_red_light); - - mActionEncryptFile.setVisibility(View.GONE); - mActionEncryptText.setVisibility(View.GONE); - mActionNfc.setVisibility(View.GONE); - mFab.setVisibility(View.GONE); - mQrCodeLayout.setVisibility(View.GONE); - } else if (mIsSecret) { - mStatusText.setText(R.string.view_key_my_key); - mStatusImage.setVisibility(View.GONE); - color = getResources().getColor(R.color.primary); - // reload qr code only if the fingerprint changed - if (!mFingerprint.equals(mQrCodeLoaded)) { - loadQrCode(mFingerprint); - } - photoTask.execute(mMasterKeyId); - mQrCodeLayout.setVisibility(View.VISIBLE); - - // and place leftOf qr code - RelativeLayout.LayoutParams nameParams = (RelativeLayout.LayoutParams) - mName.getLayoutParams(); - // remove right margin - nameParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - nameParams.setMarginEnd(0); + mCollapsingToolbarLayout.setTitle(getString(R.string.user_id_no_name)); } - nameParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); - mName.setLayoutParams(nameParams); - - RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams) - mStatusText.getLayoutParams(); - statusParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - statusParams.setMarginEnd(0); - } - statusParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); - mStatusText.setLayoutParams(statusParams); - mActionEncryptFile.setVisibility(View.VISIBLE); - mActionEncryptText.setVisibility(View.VISIBLE); + mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); + mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT)); + + // if it wasn't shown yet, display yubikey fragment + if (mShowYubikeyAfterCreation && getIntent().hasExtra(EXTRA_NFC_AID)) { + mShowYubikeyAfterCreation = false; + Intent intent = getIntent(); + byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS); + String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID); + byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID); + showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); + } - // invokeBeam is available from API 21 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - mActionNfc.setVisibility(View.VISIBLE); - } else { - mActionNfc.setVisibility(View.GONE); + mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; + mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; + mIsRevoked = data.getInt(INDEX_IS_REVOKED) > 0; + mIsExpired = data.getInt(INDEX_IS_EXPIRED) != 0; + mIsVerified = data.getInt(INDEX_VERIFIED) > 0; + + // if the refresh animation isn't playing + if (!mRotate.hasStarted() && !mRotateSpin.hasStarted()) { + // re-create options menu based on mIsSecret, mIsVerified + supportInvalidateOptionsMenu(); + // this is done at the end of the animation otherwise } - mFab.setVisibility(View.VISIBLE); - mFab.setIconDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); - } else { - mActionEncryptFile.setVisibility(View.VISIBLE); - mActionEncryptText.setVisibility(View.VISIBLE); - mQrCodeLayout.setVisibility(View.GONE); - mActionNfc.setVisibility(View.GONE); - if (mIsVerified) { - mStatusText.setText(R.string.view_key_verified); + AsyncTask<Long, Void, Bitmap> photoTask = + new AsyncTask<Long, Void, Bitmap>() { + protected Bitmap doInBackground(Long... mMasterKeyId) { + return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(), + mMasterKeyId[0], true); + } + + protected void onPostExecute(Bitmap photo) { + if (photo == null) { + return; + } + + mPhoto.setImageBitmap(photo); + mPhotoLayout.setVisibility(View.VISIBLE); + } + }; + + // Note: order is important + int color; + if (mIsRevoked) { + mStatusText.setText(R.string.view_key_revoked); mStatusImage.setVisibility(View.VISIBLE); KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, - State.VERIFIED, R.color.icons, true); - color = getResources().getColor(R.color.primary); - photoTask.execute(mMasterKeyId); + State.REVOKED, R.color.icons, true); + color = getResources().getColor(R.color.key_flag_red); + mActionEncryptFile.setVisibility(View.INVISIBLE); + mActionEncryptText.setVisibility(View.INVISIBLE); + mActionNfc.setVisibility(View.INVISIBLE); mFab.setVisibility(View.GONE); - } else { - mStatusText.setText(R.string.view_key_unverified); + mQrCodeLayout.setVisibility(View.GONE); + } else if (mIsExpired) { + if (mIsSecret) { + mStatusText.setText(R.string.view_key_expired_secret); + } else { + mStatusText.setText(R.string.view_key_expired); + } mStatusImage.setVisibility(View.VISIBLE); KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, - State.UNVERIFIED, R.color.icons, true); - color = getResources().getColor(R.color.android_orange_light); + State.EXPIRED, R.color.icons, true); + color = getResources().getColor(R.color.key_flag_red); + mActionEncryptFile.setVisibility(View.INVISIBLE); + mActionEncryptText.setVisibility(View.INVISIBLE); + mActionNfc.setVisibility(View.INVISIBLE); + mFab.setVisibility(View.GONE); + mQrCodeLayout.setVisibility(View.GONE); + } else if (mIsSecret) { + mStatusText.setText(R.string.view_key_my_key); + mStatusImage.setVisibility(View.GONE); + color = getResources().getColor(R.color.key_flag_green); + // reload qr code only if the fingerprint changed + if (!mFingerprint.equals(mQrCodeLoaded)) { + loadQrCode(mFingerprint); + } + photoTask.execute(mMasterKeyId); + mQrCodeLayout.setVisibility(View.VISIBLE); + + // and place leftOf qr code +// RelativeLayout.LayoutParams nameParams = (RelativeLayout.LayoutParams) +// mName.getLayoutParams(); +// // remove right margin +// nameParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); +// if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { +// nameParams.setMarginEnd(0); +// } +// nameParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); +// mName.setLayoutParams(nameParams); + + RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams) + mStatusText.getLayoutParams(); + statusParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + statusParams.setMarginEnd(0); + } + statusParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); + mStatusText.setLayoutParams(statusParams); + + mActionEncryptFile.setVisibility(View.VISIBLE); + mActionEncryptText.setVisibility(View.VISIBLE); + + // invokeBeam is available from API 21 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mActionNfc.setVisibility(View.VISIBLE); + } else { + mActionNfc.setVisibility(View.GONE); + } mFab.setVisibility(View.VISIBLE); - } - } + // noinspection deprecation (no getDrawable with theme at current minApi level 15!) + mFab.setImageDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); + } else { + mActionEncryptFile.setVisibility(View.VISIBLE); + mActionEncryptText.setVisibility(View.VISIBLE); + mQrCodeLayout.setVisibility(View.GONE); + mActionNfc.setVisibility(View.GONE); - if (mPreviousColor == 0 || mPreviousColor == color) { - mStatusBar.setBackgroundColor(color); - mBigToolbar.setBackgroundColor(color); - mPreviousColor = color; - } else { - ObjectAnimator colorFade1 = - ObjectAnimator.ofObject(mStatusBar, "backgroundColor", - new ArgbEvaluator(), mPreviousColor, color); - ObjectAnimator colorFade2 = - ObjectAnimator.ofObject(mBigToolbar, "backgroundColor", - new ArgbEvaluator(), mPreviousColor, color); - - colorFade1.setDuration(1200); - colorFade2.setDuration(1200); - colorFade1.start(); - colorFade2.start(); - mPreviousColor = color; - } + if (mIsVerified) { + mStatusText.setText(R.string.view_key_verified); + mStatusImage.setVisibility(View.VISIBLE); + KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, + State.VERIFIED, R.color.icons, true); + color = getResources().getColor(R.color.key_flag_green); + photoTask.execute(mMasterKeyId); + + mFab.setVisibility(View.GONE); + } else { + mStatusText.setText(R.string.view_key_unverified); + mStatusImage.setVisibility(View.VISIBLE); + KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, + State.UNVERIFIED, R.color.icons, true); + color = getResources().getColor(R.color.key_flag_orange); + + mFab.setVisibility(View.VISIBLE); + } + } - //noinspection deprecation - mStatusImage.setAlpha(80); + if (mPreviousColor == 0 || mPreviousColor == color) { + mAppBarLayout.setBackgroundColor(color); + mCollapsingToolbarLayout.setContentScrimColor(color); + mCollapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color)); + mPreviousColor = color; + } else { + ObjectAnimator colorFade = + ObjectAnimator.ofObject(mAppBarLayout, "backgroundColor", + new ArgbEvaluator(), mPreviousColor, color); + mCollapsingToolbarLayout.setContentScrimColor(color); + mCollapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color)); + + colorFade.setDuration(1200); + colorFade.start(); + mPreviousColor = color; + } - break; + //noinspection deprecation + mStatusImage.setAlpha(80); + break; + } } } } @@ -984,4 +951,64 @@ public class ViewKeyActivity extends BaseNfcActivity implements } + // CryptoOperationHelper.Callback functions + + + private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper) + throws ProviderHelper.NotFoundException { + + mIsRefreshing = true; + mRefreshItem.setEnabled(false); + mRefreshItem.setActionView(mRefresh); + mRefresh.startAnimation(mRotate); + + byte[] blob = (byte[]) providerHelper.getGenericData( + KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri), + KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB); + String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob); + + ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null); + ArrayList<ParcelableKeyRing> entries = new ArrayList<>(); + entries.add(keyEntry); + mKeyList = entries; + + // search config + { + Preferences prefs = Preferences.getPreferences(this); + Preferences.CloudSearchPrefs cloudPrefs = + new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); + mKeyserver = cloudPrefs.keyserver; + } + + mOperationHelper.cryptoOperation(); + } + + @Override + public ImportKeyringParcel createOperationInput() { + return new ImportKeyringParcel(mKeyList, mKeyserver); + } + + @Override + public void onCryptoOperationSuccess(ImportKeyResult result) { + mIsRefreshing = false; + result.createNotify(this).show(); + } + + @Override + public void onCryptoOperationCancelled() { + mIsRefreshing = false; + } + + @Override + public void onCryptoOperationError(ImportKeyResult result) { + mIsRefreshing = false; + result.createNotify(this).show(); + } + + @Override + public boolean onCryptoSetProgress(String msg, int progress, int max) { + return true; + } + } + |