use LiveData to load data in ViewKeyActivity

This commit is contained in:
Vincent Breitmoser 2018-06-24 01:14:07 +02:00
parent 2d1ff8cdcc
commit 5cfe0d140f
7 changed files with 235 additions and 340 deletions

View file

@ -58,5 +58,9 @@ public abstract class SubKey implements KeysModel {
public boolean has_auth_key() {
return has_auth_key_int() != 0;
}
public boolean has_encrypt_key() {
return has_encrypt_key_int() != 0;
}
}
}

View file

@ -28,9 +28,11 @@ import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityOptions;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.net.Uri;
@ -49,9 +51,6 @@ import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.widget.CardView;
import android.view.Menu;
import android.view.MenuItem;
@ -71,14 +70,14 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.livedata.GenericLiveData;
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
@ -113,7 +112,6 @@ import timber.log.Timber;
public class ViewKeyActivity extends BaseSecurityTokenActivity implements
LoaderManager.LoaderCallbacks<Cursor>,
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
@Retention(RetentionPolicy.SOURCE)
@IntDef({REQUEST_QR_FINGERPRINT, REQUEST_BACKUP, REQUEST_CERTIFY, REQUEST_DELETE})
@ -151,23 +149,13 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
private byte[] qrCodeLoaded;
private static final int LOADER_ID_UNIFIED = 0;
private boolean isSecret = false;
private boolean hasEncrypt = false;
private boolean isVerified = false;
private boolean isRevoked = false;
private boolean isSecure = true;
private boolean isExpired = false;
private UnifiedKeyInfo unifiedKeyInfo;
private MenuItem refreshItem;
private boolean isRefreshing;
private Animation rotate, rotateSpin;
private View refreshView;
private long masterKeyId;
private byte[] fingerprint;
public static Intent getViewKeyActivityIntent(@NonNull Context context, long masterKeyId) {
Intent viewIntent = new Intent(context, ViewKeyActivity.class);
viewIntent.putExtra(ViewKeyActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
@ -250,6 +238,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
});
refreshView = getLayoutInflater().inflate(R.layout.indeterminate_progress, null);
long masterKeyId;
Intent intent = getIntent();
Uri dataUri = intent.getData();
if (intent.hasExtra(EXTRA_MASTER_KEY_ID)) {
@ -271,7 +260,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
actionEncryptText.setOnClickListener(v -> encrypt(true));
floatingActionButton.setOnClickListener(v -> {
if (isSecret) {
if (unifiedKeyInfo.has_any_secret()) {
startSafeSlinger();
} else {
scanQrCode();
@ -280,9 +269,8 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
qrCodeLayout.setOnClickListener(v -> showQrCodeDialog());
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
ViewKeyViewModel viewModel = ViewModelProviders.of(this).get(ViewKeyViewModel.class);
viewModel.setMasterKeyId(getIntent().getLongExtra(EXTRA_MASTER_KEY_ID, 0L));
if (savedInstanceState == null && intent.hasExtra(EXTRA_DISPLAY_RESULT)) {
OperationResult result = intent.getParcelableExtra(EXTRA_DISPLAY_RESULT);
@ -294,8 +282,13 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
return;
}
FragmentManager manager = getSupportFragmentManager();
ViewKeyFragment frag = ViewKeyFragment.newInstance();
manager.beginTransaction().replace(R.id.view_key_fragment, frag, "view_key_fragment").commit();
if (Preferences.getPreferences(this).getExperimentalEnableKeybase()) {
FragmentManager manager = getSupportFragmentManager();
manager = getSupportFragmentManager();
final ViewKeyKeybaseFragment keybaseFrag = ViewKeyKeybaseFragment.newInstance(masterKeyId);
manager.beginTransaction()
.replace(R.id.view_key_keybase_fragment, keybaseFrag)
@ -303,6 +296,30 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
}
}
public static class ViewKeyViewModel extends ViewModel {
private Long masterKeyId;
private LiveData<UnifiedKeyInfo> unifiedKeyInfoLiveData;
void setMasterKeyId(long masterKeyId) {
if (this.masterKeyId != null) {
throw new IllegalStateException("cannot change masterKeyId once set!");
}
this.masterKeyId = masterKeyId;
}
LiveData<UnifiedKeyInfo> getUnifiedKeyInfoLiveData(Context context) {
if (masterKeyId == null) {
throw new IllegalStateException("masterKeyId must be set to retrieve this!");
}
if (unifiedKeyInfoLiveData == null) {
KeyRepository keyRepository = KeyRepository.create(context);
unifiedKeyInfoLiveData = new GenericLiveData<>(context, null,
() -> keyRepository.getUnifiedKeyInfo(masterKeyId));
}
return unifiedKeyInfoLiveData;
}
}
@Override
protected void initLayout() {
setContentView(R.layout.view_key_activity);
@ -346,7 +363,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
}
case R.id.menu_key_view_advanced: {
Intent advancedIntent = new Intent(this, ViewKeyAdvActivity.class);
advancedIntent.putExtra(ViewKeyAdvActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
advancedIntent.putExtra(ViewKeyAdvActivity.EXTRA_MASTER_KEY_ID, unifiedKeyInfo.master_key_id());
startActivity(advancedIntent);
return true;
}
@ -365,13 +382,15 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem backupKey = menu.findItem(R.id.menu_key_view_backup);
backupKey.setVisible(isSecret);
menu.findItem(R.id.menu_key_view_skt).setVisible(isSecret);
backupKey.setVisible(unifiedKeyInfo.has_any_secret());
menu.findItem(R.id.menu_key_view_skt).setVisible(unifiedKeyInfo.has_any_secret());
MenuItem changePassword = menu.findItem(R.id.menu_key_change_password);
changePassword.setVisible(isSecret);
changePassword.setVisible(unifiedKeyInfo.has_any_secret());
MenuItem certifyFingerprint = menu.findItem(R.id.menu_key_view_certify_fingerprint);
certifyFingerprint.setVisible(!isSecret && !isVerified && !isExpired && !isRevoked);
certifyFingerprint.setVisible(
!unifiedKeyInfo.has_any_secret() && !unifiedKeyInfo.is_verified() && !unifiedKeyInfo.is_expired() &&
!unifiedKeyInfo.is_revoked());
return true;
}
@ -387,6 +406,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
@Override
public void onCryptoOperationSuccess(EditKeyResult result) {
displayResult(result);
long masterKeyId = unifiedKeyInfo.master_key_id();
PassphraseCacheService.clearCachedPassphrase(getApplicationContext(), masterKeyId, masterKeyId);
}
@ -417,7 +437,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
// use new passphrase!
changeUnlockParcel = ChangeUnlockParcel.createChangeUnlockParcel(
masterKeyId, fingerprint,
unifiedKeyInfo.master_key_id(), unifiedKeyInfo.fingerprint(),
data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE)
);
@ -447,14 +467,14 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
private void certifyFingerprint() {
Intent intent = new Intent(this, CertifyFingerprintActivity.class);
intent.setData(KeyRings.buildUnifiedKeyRingUri(masterKeyId));
intent.setData(KeyRings.buildUnifiedKeyRingUri(unifiedKeyInfo.master_key_id()));
startActivityForResult(intent, REQUEST_CERTIFY);
}
private void certifyImmediate() {
Intent intent = new Intent(this, CertifyKeyActivity.class);
intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] { masterKeyId });
intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] { unifiedKeyInfo.master_key_id() });
startActivityForResult(intent, REQUEST_CERTIFY);
}
@ -471,7 +491,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
opts = options.toBundle();
}
qrCodeIntent.setData(KeyRings.buildUnifiedKeyRingUri(masterKeyId));
qrCodeIntent.setData(KeyRings.buildUnifiedKeyRingUri(unifiedKeyInfo.master_key_id()));
ActivityCompat.startActivity(this, qrCodeIntent, opts);
}
@ -479,6 +499,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
if (keyHasPassphrase()) {
Intent intent = new Intent(this, PassphraseDialogActivity.class);
long masterKeyId = unifiedKeyInfo.master_key_id();
RequiredInputParcel requiredInput =
RequiredInputParcel.createRequiredDecryptPassphrase(masterKeyId, masterKeyId);
requiredInput.mSkipCaching = true;
@ -491,6 +512,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
private boolean keyHasPassphrase() {
try {
long masterKeyId = unifiedKeyInfo.master_key_id();
SecretKeyType secretKeyType =
keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretKeyType(masterKeyId);
switch (secretKeyType) {
@ -510,7 +532,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
private void startBackupActivity() {
Intent intent = new Intent(this, BackupActivity.class);
intent.putExtra(BackupActivity.EXTRA_MASTER_KEY_IDS, new long[]{ masterKeyId });
intent.putExtra(BackupActivity.EXTRA_MASTER_KEY_IDS, new long[] { unifiedKeyInfo.master_key_id() });
intent.putExtra(BackupActivity.EXTRA_SECRET, true);
startActivity(intent);
}
@ -519,9 +541,9 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
Intent deleteIntent = new Intent(this, DeleteKeyDialogActivity.class);
deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS,
new long[]{ masterKeyId });
deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, isSecret);
if (isSecret) {
new long[]{ unifiedKeyInfo.master_key_id() });
deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, unifiedKeyInfo.has_any_secret());
if (unifiedKeyInfo.has_any_secret()) {
// for upload in case key is secret
deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_KEYSERVER,
Preferences.getPreferences(this).getPreferredKeyserver());
@ -559,7 +581,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
Notify.create(this, R.string.error_scan_fp, Notify.LENGTH_LONG, Style.ERROR).show();
return;
}
if (Arrays.equals(this.fingerprint, fingerprint)) {
if (Arrays.equals(unifiedKeyInfo.fingerprint(), fingerprint)) {
certifyImmediate();
} else {
Notify.create(this, R.string.error_scan_match, Notify.LENGTH_LONG, Style.ERROR).show();
@ -598,35 +620,13 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
finish();
}
public void showMainFragment() {
new Handler().post(() -> {
FragmentManager manager = getSupportFragmentManager();
// unless we must refresh
ViewKeyFragment frag = (ViewKeyFragment) manager.findFragmentByTag("view_key_fragment");
// if everything is valid, just drop it
if (frag != null && frag.isValidForData(isSecret)) {
return;
}
// if the main fragment doesn't exist, or is not of the correct type, (re)create it
frag = ViewKeyFragment.newInstance(masterKeyId, isSecret);
// get rid of possible backstack, this fragment is always at the bottom
manager.popBackStack("security_token", FragmentManager.POP_BACK_STACK_INCLUSIVE);
manager.beginTransaction()
.replace(R.id.view_key_fragment, frag, "view_key_fragment")
// if this gets lost, it doesn't really matter since the loader will reinstate it onResume
.commitAllowingStateLoss();
});
}
private void encrypt(boolean text) {
// If there is no encryption key, don't bother.
if (!hasEncrypt) {
if (!unifiedKeyInfo.has_encrypt_key()) {
Notify.create(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR).show();
return;
}
long[] encryptionKeyIds = new long[] { masterKeyId };
long[] encryptionKeyIds = new long[] { unifiedKeyInfo.master_key_id() };
Intent intent;
if (text) {
intent = new Intent(this, EncryptTextActivity.class);
@ -643,7 +643,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
private void startSafeSlinger() {
Intent safeSlingerIntent = new Intent(this, SafeSlingerActivity.class);
safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, unifiedKeyInfo.master_key_id());
startActivityForResult(safeSlingerIntent, 0);
}
@ -682,50 +682,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
loadTask.execute();
}
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[]{
KeychainContract.KeyRings._ID,
KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.KeyRings.USER_ID,
KeychainContract.KeyRings.IS_REVOKED,
KeychainContract.KeyRings.IS_EXPIRED,
KeychainContract.KeyRings.IS_SECURE,
KeychainContract.KeyRings.VERIFIED,
KeychainContract.KeyRings.HAS_ANY_SECRET,
KeychainContract.KeyRings.FINGERPRINT,
KeychainContract.KeyRings.HAS_ENCRYPT,
KeyRings.NAME,
KeyRings.EMAIL,
KeyRings.COMMENT
};
static final int INDEX_MASTER_KEY_ID = 1;
static final int INDEX_USER_ID = 2;
static final int INDEX_IS_REVOKED = 3;
static final int INDEX_IS_EXPIRED = 4;
static final int INDEX_IS_SECURE = 5;
static final int INDEX_VERIFIED = 6;
static final int INDEX_HAS_ANY_SECRET = 7;
static final int INDEX_FINGERPRINT = 8;
static final int INDEX_HAS_ENCRYPT = 9;
static final int INDEX_NAME = 10;
static final int INDEX_EMAIL = 11;
static final int INDEX_COMMENT = 12;
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
case LOADER_ID_UNIFIED: {
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(masterKeyId);
return new CursorLoader(this, baseUri, PROJECTION, null, null, null);
}
default:
return null;
}
}
int mPreviousColor = 0;
/**
@ -747,127 +703,104 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
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,
* because the notification triggers faster than the activity closes.
*/
private void onLoadUnifiedKeyInfo(UnifiedKeyInfo unifiedKeyInfo) {
if (unifiedKeyInfo == null) {
return;
}
// 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: {
// Avoid NullPointerExceptions...
if (data.getCount() == 0) {
return;
}
this.unifiedKeyInfo = unifiedKeyInfo;
if (data.moveToFirst()) {
// get name, email, and comment from USER_ID
String name = unifiedKeyInfo.name();
collapsingToolbarLayout.setTitle(name != null ? name : getString(R.string.user_id_no_name));
String name = data.getString(INDEX_NAME);
// if the refresh animation isn't playing
if (!rotate.hasStarted() && !rotateSpin.hasStarted()) {
// re-create options menu based on isSecret, isVerified
supportInvalidateOptionsMenu();
// this is done at the end of the animation otherwise
}
collapsingToolbarLayout.setTitle(name != null ? name : getString(R.string.user_id_no_name));
masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
fingerprint = data.getBlob(INDEX_FINGERPRINT);
isSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
hasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0;
isRevoked = data.getInt(INDEX_IS_REVOKED) > 0;
isExpired = data.getInt(INDEX_IS_EXPIRED) != 0;
isSecure = data.getInt(INDEX_IS_SECURE) == 1;
isVerified = data.getInt(INDEX_VERIFIED) > 0;
// queue showing of the main fragment
showMainFragment();
// if the refresh animation isn't playing
if (!rotate.hasStarted() && !rotateSpin.hasStarted()) {
// re-create options menu based on isSecret, isVerified
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 new ContactHelper(ViewKeyActivity.this)
.loadPhotoByMasterKeyId(mMasterKeyId[0], true);
}
AsyncTask<Long, Void, Bitmap> photoTask =
new AsyncTask<Long, Void, Bitmap>() {
protected Bitmap doInBackground(Long... mMasterKeyId) {
return new ContactHelper(ViewKeyActivity.this)
.loadPhotoByMasterKeyId(mMasterKeyId[0], true);
}
protected void onPostExecute(Bitmap photo) {
if (photo == null) {
return;
}
photoView.setImageBitmap(photo);
photoView.setColorFilter(getResources().getColor(R.color.toolbar_photo_tint), PorterDuff.Mode.SRC_ATOP);
photoLayout.setVisibility(View.VISIBLE);
}
};
boolean showStatusText = isSecure && !isExpired && !isRevoked;
if (showStatusText) {
statusText.setVisibility(View.VISIBLE);
if (isSecret) {
statusText.setText(R.string.view_key_my_key);
} else if (isVerified) {
statusText.setText(R.string.view_key_verified);
} else {
statusText.setText(R.string.view_key_unverified);
protected void onPostExecute(Bitmap photo) {
if (photo == null) {
return;
}
} else {
statusText.setVisibility(View.GONE);
photoView.setImageBitmap(photo);
photoView.setColorFilter(getResources().getColor(R.color.toolbar_photo_tint),
PorterDuff.Mode.SRC_ATOP);
photoLayout.setVisibility(View.VISIBLE);
}
};
// Note: order is important
int color;
if (isRevoked) {
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.REVOKED, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_red);
boolean showStatusText = unifiedKeyInfo.is_secure() && !unifiedKeyInfo.is_expired() && !unifiedKeyInfo.is_revoked();
if (showStatusText) {
statusText.setVisibility(View.VISIBLE);
actionEncryptFile.setVisibility(View.INVISIBLE);
actionEncryptText.setVisibility(View.INVISIBLE);
hideFab();
qrCodeLayout.setVisibility(View.GONE);
} else if (!isSecure) {
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.INSECURE, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_red);
if (unifiedKeyInfo.has_any_secret()) {
statusText.setText(R.string.view_key_my_key);
} else if (unifiedKeyInfo.is_verified()) {
statusText.setText(R.string.view_key_verified);
} else {
statusText.setText(R.string.view_key_unverified);
}
} else {
statusText.setVisibility(View.GONE);
}
actionEncryptFile.setVisibility(View.INVISIBLE);
actionEncryptText.setVisibility(View.INVISIBLE);
hideFab();
qrCodeLayout.setVisibility(View.GONE);
} else if (isExpired) {
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.EXPIRED, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_red);
// Note: order is important
int color;
if (unifiedKeyInfo.is_revoked()) {
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.REVOKED, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_red);
actionEncryptFile.setVisibility(View.INVISIBLE);
actionEncryptText.setVisibility(View.INVISIBLE);
hideFab();
qrCodeLayout.setVisibility(View.GONE);
} else if (isSecret) {
statusImage.setVisibility(View.GONE);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_green);
// reload qr code only if the fingerprint changed
if (!Arrays.equals(fingerprint, qrCodeLoaded)) {
loadQrCode(fingerprint);
}
photoTask.execute(masterKeyId);
qrCodeLayout.setVisibility(View.VISIBLE);
actionEncryptFile.setVisibility(View.INVISIBLE);
actionEncryptText.setVisibility(View.INVISIBLE);
hideFab();
qrCodeLayout.setVisibility(View.GONE);
} else if (!unifiedKeyInfo.is_secure()) {
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.INSECURE, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_red);
// and place leftOf qr code
actionEncryptFile.setVisibility(View.INVISIBLE);
actionEncryptText.setVisibility(View.INVISIBLE);
hideFab();
qrCodeLayout.setVisibility(View.GONE);
} else if (unifiedKeyInfo.is_expired()) {
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.EXPIRED, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_red);
actionEncryptFile.setVisibility(View.INVISIBLE);
actionEncryptText.setVisibility(View.INVISIBLE);
hideFab();
qrCodeLayout.setVisibility(View.GONE);
} else if (unifiedKeyInfo.has_any_secret()) {
statusImage.setVisibility(View.GONE);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_green);
// reload qr code only if the fingerprint changed
if (!Arrays.equals(unifiedKeyInfo.fingerprint(), qrCodeLoaded)) {
loadQrCode(unifiedKeyInfo.fingerprint());
}
photoTask.execute(unifiedKeyInfo.master_key_id());
qrCodeLayout.setVisibility(View.VISIBLE);
// and place leftOf qr code
// RelativeLayout.LayoutParams nameParams = (RelativeLayout.LayoutParams)
// mName.getLayoutParams();
// // remove right margin
@ -878,72 +811,67 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
// nameParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout);
// mName.setLayoutParams(nameParams);
RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams)
statusText.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);
statusText.setLayoutParams(statusParams);
RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams)
statusText.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);
statusText.setLayoutParams(statusParams);
actionEncryptFile.setVisibility(View.VISIBLE);
actionEncryptText.setVisibility(View.VISIBLE);
actionEncryptFile.setVisibility(View.VISIBLE);
actionEncryptText.setVisibility(View.VISIBLE);
showFab();
// noinspection deprecation (no getDrawable with theme at current minApi level 15!)
floatingActionButton.setImageDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp));
} else {
actionEncryptFile.setVisibility(View.VISIBLE);
actionEncryptText.setVisibility(View.VISIBLE);
qrCodeLayout.setVisibility(View.GONE);
showFab();
// noinspection deprecation (no getDrawable with theme at current minApi level 15!)
floatingActionButton.setImageDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp));
} else {
actionEncryptFile.setVisibility(View.VISIBLE);
actionEncryptText.setVisibility(View.VISIBLE);
qrCodeLayout.setVisibility(View.GONE);
if (isVerified) {
statusText.setText(R.string.view_key_verified);
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.VERIFIED, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_green);
photoTask.execute(masterKeyId);
if (unifiedKeyInfo.is_verified()) {
statusText.setText(R.string.view_key_verified);
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.VERIFIED, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_green);
photoTask.execute(unifiedKeyInfo.master_key_id());
hideFab();
} else {
statusText.setText(R.string.view_key_unverified);
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.UNVERIFIED, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_orange);
hideFab();
} else {
statusText.setText(R.string.view_key_unverified);
statusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, statusImage, statusText,
State.UNVERIFIED, R.color.icons, true);
// noinspection deprecation, fix requires api level 23
color = getResources().getColor(R.color.key_flag_orange);
showFab();
}
}
if (mPreviousColor == 0 || mPreviousColor == color) {
appBarLayout.setBackgroundColor(color);
collapsingToolbarLayout.setContentScrimColor(color);
collapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color));
mPreviousColor = color;
} else {
ObjectAnimator colorFade =
ObjectAnimator.ofObject(appBarLayout, "backgroundColor",
new ArgbEvaluator(), mPreviousColor, color);
collapsingToolbarLayout.setContentScrimColor(color);
collapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color));
colorFade.setDuration(1200);
colorFade.start();
mPreviousColor = color;
}
//noinspection deprecation
statusImage.setAlpha(80);
break;
}
showFab();
}
}
if (mPreviousColor == 0 || mPreviousColor == color) {
appBarLayout.setBackgroundColor(color);
collapsingToolbarLayout.setContentScrimColor(color);
collapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color));
mPreviousColor = color;
} else {
ObjectAnimator colorFade =
ObjectAnimator.ofObject(appBarLayout, "backgroundColor",
new ArgbEvaluator(), mPreviousColor, color);
collapsingToolbarLayout.setContentScrimColor(color);
collapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color));
colorFade.setDuration(1200);
colorFade.start();
mPreviousColor = color;
}
//noinspection deprecation
statusImage.setAlpha(80);
}
/**
@ -968,16 +896,11 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
floatingActionButton.setVisibility(View.GONE);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
// CryptoOperationHelper.Callback functions
private void updateFromKeyserver() {
if (fingerprint == null) {
if (unifiedKeyInfo == null) {
return;
}
@ -993,7 +916,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
public ImportKeyringParcel createOperationInput() {
HkpKeyserverAddress preferredKeyserver = Preferences.getPreferences(this).getPreferredKeyserver();
ParcelableKeyRing keyEntry = ParcelableKeyRing.createFromReference(fingerprint, null, null, null);
ParcelableKeyRing keyEntry = ParcelableKeyRing.createFromReference(unifiedKeyInfo.fingerprint(), null, null, null);
return ImportKeyringParcel.createImportKeyringParcel(Collections.singletonList(keyEntry), preferredKeyserver);
}

View file

@ -26,11 +26,11 @@ import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.PopupMenu.OnDismissListener;
import android.support.v7.widget.PopupMenu.OnMenuItemClickListener;
import android.view.LayoutInflater;
import android.view.MenuItem;
@ -40,8 +40,9 @@ import android.view.ViewGroup;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.model.KeyMetadata;
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.ui.base.LoaderFragment;
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity.ViewKeyViewModel;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
@ -56,12 +57,7 @@ import org.sufficientlysecure.keychain.ui.keyview.view.KeyserverStatusView;
import org.sufficientlysecure.keychain.ui.keyview.view.SystemContactCardView;
public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, OnMenuItemClickListener {
public static final String ARG_MASTER_KEY_ID = "master_key_id";
public static final String ARG_IS_SECRET = "is_secret";
boolean mIsSecret = false;
public class ViewKeyFragment extends Fragment implements ViewKeyMvpView, OnMenuItemClickListener {
private IdentitiesCardView identitiesCardView;
private IdentitiesPresenter identitiesPresenter;
@ -76,32 +72,20 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, O
private Integer displayedContextMenuPosition;
/**
* Creates new instance of this fragment
*/
public static ViewKeyFragment newInstance(long masterKeyId, boolean isSecret) {
ViewKeyFragment frag = new ViewKeyFragment();
Bundle args = new Bundle();
args.putLong(ARG_MASTER_KEY_ID, masterKeyId);
args.putBoolean(ARG_IS_SECRET, isSecret);
frag.setArguments(args);
return frag;
public static ViewKeyFragment newInstance() {
return new ViewKeyFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
View view = inflater.inflate(R.layout.view_key_fragment, getContainer());
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.view_key_fragment, viewGroup, false);
identitiesCardView = view.findViewById(R.id.card_identities);
systemContactCard = view.findViewById(R.id.linked_system_contact_card);
keyStatusHealth = view.findViewById(R.id.key_status_health);
keyStatusKeyserver = view.findViewById(R.id.key_status_keyserver);
return root;
return view;
}
public static class KeyFragmentViewModel extends ViewModel {
@ -143,39 +127,36 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, O
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
long masterKeyId = getArguments().getLong(ARG_MASTER_KEY_ID);
mIsSecret = getArguments().getBoolean(ARG_IS_SECRET);
ViewKeyViewModel viewKeyViewModel = ViewModelProviders.of(requireActivity()).get(ViewKeyViewModel.class);
viewKeyViewModel.getUnifiedKeyInfoLiveData(requireContext()).observe(this, this::onLoadUnifiedKeyInfo);
}
private void onLoadUnifiedKeyInfo(UnifiedKeyInfo unifiedKeyInfo) {
KeyFragmentViewModel model = ViewModelProviders.of(this).get(KeyFragmentViewModel.class);
identitiesPresenter = new IdentitiesPresenter(
getContext(), identitiesCardView, this, masterKeyId, mIsSecret);
getContext(), identitiesCardView, this, unifiedKeyInfo.master_key_id(), unifiedKeyInfo.has_any_secret());
model.getIdentityInfo(identitiesPresenter).observe(this, identitiesPresenter);
systemContactPresenter = new SystemContactPresenter(
getContext(), systemContactCard, masterKeyId, mIsSecret);
getContext(), systemContactCard, unifiedKeyInfo.master_key_id(), unifiedKeyInfo.has_any_secret());
model.getSystemContactInfo(systemContactPresenter).observe(this, systemContactPresenter);
keyHealthPresenter = new KeyHealthPresenter(getContext(), keyStatusHealth, masterKeyId);
keyHealthPresenter = new KeyHealthPresenter(getContext(), keyStatusHealth, unifiedKeyInfo.master_key_id());
model.getSubkeyStatus(keyHealthPresenter).observe(this, keyHealthPresenter);
keyserverStatusPresenter = new KeyserverStatusPresenter(
getContext(), keyStatusKeyserver, masterKeyId, mIsSecret);
getContext(), keyStatusKeyserver, unifiedKeyInfo.master_key_id(), unifiedKeyInfo.has_any_secret());
model.getKeyserverStatus(keyserverStatusPresenter).observe(this, keyserverStatusPresenter);
}
@Override
public void switchToFragment(final Fragment frag, final String backStackName) {
new Handler().post(new Runnable() {
@Override
public void run() {
getFragmentManager().beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.view_key_fragment, frag)
.addToBackStack(backStackName)
.commit();
}
});
new Handler().post(() -> requireFragmentManager().beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.view_key_fragment, frag)
.addToBackStack(backStackName)
.commit());
}
@Override
@ -189,10 +170,6 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, O
}
}
public boolean isValidForData(boolean isSecret) {
return isSecret == mIsSecret;
}
@Override
public void startActivityAndShowResultSnackbar(Intent intent) {
startActivityForResult(intent, 0);
@ -200,26 +177,18 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, O
@Override
public void showDialogFragment(final DialogFragment dialogFragment, final String tag) {
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
dialogFragment.show(getActivity().getSupportFragmentManager(), tag);
}
});
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(
() -> dialogFragment.show(requireFragmentManager(), tag));
}
@Override
public void showContextMenu(int position, View anchor) {
displayedContextMenuPosition = position;
PopupMenu menu = new PopupMenu(getContext(), anchor);
PopupMenu menu = new PopupMenu(requireContext(), anchor);
menu.inflate(R.menu.identity_context_menu);
menu.setOnMenuItemClickListener(this);
menu.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(PopupMenu popupMenu) {
displayedContextMenuPosition = null;
}
});
menu.setOnDismissListener(popupMenu -> displayedContextMenuPosition = null);
menu.show();
}

View file

@ -88,7 +88,6 @@ public class IdentitiesPresenter implements Observer<List<IdentityInfo>> {
@Override
public void onChanged(@Nullable List<IdentityInfo> identityInfos) {
viewKeyMvpView.setContentShown(true, false);
identitiesAdapter.setData(identityInfos);
}

View file

@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.util.Comparator;
import java.util.Date;
import android.app.Application;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;

View file

@ -30,7 +30,6 @@ public interface ViewKeyMvpView {
void startActivity(Intent intent);
void startActivityAndShowResultSnackbar(Intent intent);
void showDialogFragment(DialogFragment dialogFragment, final String tag);
void setContentShown(boolean show, boolean animate);
void showContextMenu(int position, View anchor);
}

View file

@ -28,6 +28,7 @@ SELECT keys.master_key_id, keys.fingerprint, MIN(user_packets.rank), user_packet
(EXISTS (SELECT * FROM user_packets AS dups WHERE dups.master_key_id != keys.master_key_id AND dups.rank = 0 AND dups.name = user_packets.name COLLATE NOCASE AND dups.email = user_packets.email COLLATE NOCASE )) AS has_duplicate_int,
(EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.has_secret != 0 )) AS has_any_secret_int,
(EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.can_authenticate != 0 )) AS has_auth_key_int,
(EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.can_encrypt != 0 )) AS has_encrypt_key_int,
GROUP_CONCAT(DISTINCT aTI.package_name) AS autocrypt_package_names_csv,
GROUP_CONCAT(user_packets.user_id, '|||') AS user_id_list
FROM keys
@ -43,6 +44,7 @@ SELECT keys.master_key_id, keys.fingerprint, MIN(user_packets.rank), user_packet
(EXISTS (SELECT * FROM user_packets AS dups WHERE dups.master_key_id != keys.master_key_id AND dups.rank = 0 AND dups.name = user_packets.name COLLATE NOCASE AND dups.email = user_packets.email COLLATE NOCASE )) AS has_duplicate_int,
(EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.has_secret != 0 )) AS has_any_secret_int,
(EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.can_authenticate != 0 )) AS has_auth_key_int,
(EXISTS (SELECT * FROM keys AS k WHERE k.master_key_id = keys.master_key_id AND k.can_encrypt != 0 )) AS has_encrypt_key_int,
GROUP_CONCAT(DISTINCT aTI.package_name) AS autocrypt_package_names_csv,
GROUP_CONCAT(user_packets.user_id, '|||') AS user_id_list
FROM keys