Merge pull request #2348 from open-keychain/live-data

Use LiveData in favor of ContentLoader in ViewKeyFragment
This commit is contained in:
Dominik Schürmann
2018-06-19 16:44:06 +02:00
committed by GitHub
21 changed files with 437 additions and 469 deletions

View File

@@ -72,10 +72,11 @@ public class AutocryptPeerDataAccessObject {
private final SimpleContentResolverInterface queryInterface;
private final String packageName;
private final DatabaseNotifyManager databaseNotifyManager;
public AutocryptPeerDataAccessObject(Context context, String packageName) {
this.packageName = packageName;
this.databaseNotifyManager = DatabaseNotifyManager.create(context);
final ContentResolver contentResolver = context.getContentResolver();
queryInterface = new SimpleContentResolverInterface() {
@@ -102,9 +103,11 @@ public class AutocryptPeerDataAccessObject {
};
}
public AutocryptPeerDataAccessObject(SimpleContentResolverInterface queryInterface, String packageName) {
public AutocryptPeerDataAccessObject(SimpleContentResolverInterface queryInterface, String packageName,
DatabaseNotifyManager databaseNotifyManager) {
this.queryInterface = queryInterface;
this.packageName = packageName;
this.databaseNotifyManager = databaseNotifyManager;
}
public Long getMasterKeyIdForAutocryptPeer(String autocryptId) {
@@ -190,6 +193,7 @@ public class AutocryptPeerDataAccessObject {
cv.put(ApiAutocryptPeer.IS_MUTUAL, isMutual ? 1 : 0);
queryInterface
.update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null);
databaseNotifyManager.notifyAutocryptUpdate(autocryptId, masterKeyId);
}
public void updateKeyGossipFromAutocrypt(String autocryptId, Date effectiveDate, long masterKeyId) {
@@ -211,10 +215,13 @@ public class AutocryptPeerDataAccessObject {
cv.put(ApiAutocryptPeer.GOSSIP_ORIGIN, origin);
queryInterface
.update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null);
databaseNotifyManager.notifyAutocryptUpdate(autocryptId, masterKeyId);
}
public void delete(String autocryptId) {
Long masterKeyId = getMasterKeyIdForAutocryptPeer(autocryptId);
queryInterface.delete(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), null, null);
databaseNotifyManager.notifyAutocryptDelete(autocryptId, masterKeyId);
}
public List<AutocryptRecommendationResult> determineAutocryptRecommendations(String... autocryptIds) {

View File

@@ -0,0 +1,42 @@
package org.sufficientlysecure.keychain.provider;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
public class DatabaseNotifyManager {
private ContentResolver contentResolver;
public static DatabaseNotifyManager create(Context context) {
ContentResolver contentResolver = context.getContentResolver();
return new DatabaseNotifyManager(contentResolver);
}
private DatabaseNotifyManager(ContentResolver contentResolver) {
this.contentResolver = contentResolver;
}
public void notifyKeyChange(long masterKeyId) {
Uri uri = KeyRings.buildGenericKeyRingUri(masterKeyId);
contentResolver.notifyChange(uri, null);
}
public void notifyAutocryptDelete(String autocryptId, Long masterKeyId) {
Uri uri = KeyRings.buildGenericKeyRingUri(masterKeyId);
contentResolver.notifyChange(uri, null);
}
public void notifyAutocryptUpdate(String autocryptId, long masterKeyId) {
Uri uri = KeyRings.buildGenericKeyRingUri(masterKeyId);
contentResolver.notifyChange(uri, null);
}
public void notifyKeyserverStatusChange(long masterKeyId) {
Uri uri = KeyRings.buildGenericKeyRingUri(masterKeyId);
contentResolver.notifyChange(uri, null);
}
}

View File

@@ -53,7 +53,7 @@ public class KeyRepository {
public static final int FIELD_TYPE_STRING = 4;
public static final int FIELD_TYPE_BLOB = 5;
final ContentResolver mContentResolver;
final ContentResolver contentResolver;
final LocalPublicKeyStorage mLocalPublicKeyStorage;
OperationLog mLog;
int mIndent;
@@ -71,7 +71,7 @@ public class KeyRepository {
KeyRepository(ContentResolver contentResolver, LocalPublicKeyStorage localPublicKeyStorage,
OperationLog log, int indent) {
mContentResolver = contentResolver;
this.contentResolver = contentResolver;
mLocalPublicKeyStorage = localPublicKeyStorage;
mIndent = indent;
mLog = log;
@@ -121,7 +121,7 @@ public class KeyRepository {
private HashMap<String, Object> getGenericData(Uri uri, String[] proj, int[] types, String selection)
throws NotFoundException {
Cursor cursor = mContentResolver.query(uri, proj, selection, null, null);
Cursor cursor = contentResolver.query(uri, proj, selection, null, null);
try {
HashMap<String, Object> result = new HashMap<>(proj.length);
@@ -184,7 +184,7 @@ public class KeyRepository {
}
public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri queryUri) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri,
Cursor cursor = contentResolver.query(queryUri,
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.VERIFIED }, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
@@ -208,7 +208,7 @@ public class KeyRepository {
}
public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri queryUri) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri,
Cursor cursor = contentResolver.query(queryUri,
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.VERIFIED, KeyRings.HAS_ANY_SECRET }, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
@@ -232,7 +232,7 @@ public class KeyRepository {
}
public ArrayList<String> getConfirmedUserIds(long masterKeyId) throws NotFoundException {
Cursor cursor = mContentResolver.query(UserPackets.buildUserIdsUri(masterKeyId),
Cursor cursor = contentResolver.query(UserPackets.buildUserIdsUri(masterKeyId),
new String[]{UserPackets.USER_ID}, UserPackets.VERIFIED + " = " + Certs.VERIFIED_SECRET, null, null
);
if (cursor == null) {
@@ -276,12 +276,12 @@ public class KeyRepository {
}
public ContentResolver getContentResolver() {
return mContentResolver;
return contentResolver;
}
@Nullable
Long getLastUpdateTime(long masterKeyId) {
Cursor lastUpdatedCursor = mContentResolver.query(
Cursor lastUpdatedCursor = contentResolver.query(
UpdatedKeys.CONTENT_URI,
new String[] { UpdatedKeys.LAST_UPDATED },
UpdatedKeys.MASTER_KEY_ID + " = ?",

View File

@@ -83,32 +83,38 @@ import timber.log.Timber;
public class KeyWritableRepository extends KeyRepository {
private static final int MAX_CACHED_KEY_SIZE = 1024 * 50;
private final Context mContext;
private final Context context;
private final LastUpdateInteractor lastUpdateInteractor;
private DatabaseNotifyManager databaseNotifyManager;
public static KeyWritableRepository create(Context context) {
LocalPublicKeyStorage localPublicKeyStorage = LocalPublicKeyStorage.getInstance(context);
LastUpdateInteractor lastUpdateInteractor = LastUpdateInteractor.create(context);
DatabaseNotifyManager databaseNotifyManager = DatabaseNotifyManager.create(context);
return new KeyWritableRepository(context, localPublicKeyStorage, lastUpdateInteractor);
return new KeyWritableRepository(context, localPublicKeyStorage, lastUpdateInteractor,
databaseNotifyManager);
}
@VisibleForTesting
KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage,
LastUpdateInteractor lastUpdateInteractor) {
this(context, localPublicKeyStorage, lastUpdateInteractor, new OperationLog(), 0);
LastUpdateInteractor lastUpdateInteractor, DatabaseNotifyManager databaseNotifyManager) {
this(context, localPublicKeyStorage, lastUpdateInteractor, new OperationLog(), 0,
databaseNotifyManager);
}
private KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage,
LastUpdateInteractor lastUpdateInteractor, OperationLog log, int indent) {
LastUpdateInteractor lastUpdateInteractor, OperationLog log, int indent,
DatabaseNotifyManager databaseNotifyManager) {
super(context.getContentResolver(), localPublicKeyStorage, log, indent);
mContext = context;
this.context = context;
this.databaseNotifyManager = databaseNotifyManager;
this.lastUpdateInteractor = lastUpdateInteractor;
}
private LongSparseArray<CanonicalizedPublicKey> getTrustedMasterKeys() {
Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] {
Cursor cursor = contentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] {
KeyRings.MASTER_KEY_ID,
// we pick from cache only information that is not easily available from keyrings
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED
@@ -530,7 +536,7 @@ public class KeyWritableRepository extends KeyRepository {
try {
// delete old version of this keyRing (from database only!), which also deletes all keys and userIds on cascade
int deleted = mContentResolver.delete(
int deleted = contentResolver.delete(
KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null);
if (deleted > 0) {
log(LogType.MSG_IP_DELETE_OLD_OK);
@@ -540,7 +546,8 @@ public class KeyWritableRepository extends KeyRepository {
}
log(LogType.MSG_IP_APPLY_BATCH);
mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations);
contentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations);
databaseNotifyManager.notifyKeyChange(masterKeyId);
log(LogType.MSG_IP_SUCCESS);
return result;
@@ -603,7 +610,7 @@ public class KeyWritableRepository extends KeyRepository {
// insert new version of this keyRing
Uri uri = KeyRingData.buildSecretKeyRingUri(masterKeyId);
return mContentResolver.insert(uri, values);
return contentResolver.insert(uri, values);
}
public boolean deleteKeyRing(long masterKeyId) {
@@ -613,8 +620,11 @@ public class KeyWritableRepository extends KeyRepository {
Timber.e(e, "Could not delete file!");
return false;
}
mContentResolver.delete(ApiAutocryptPeer.buildByMasterKeyId(masterKeyId),null, null);
int deletedRows = mContentResolver.delete(KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null);
contentResolver.delete(ApiAutocryptPeer.buildByMasterKeyId(masterKeyId),null, null);
int deletedRows = contentResolver.delete(KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null);
databaseNotifyManager.notifyKeyChange(masterKeyId);
return deletedRows > 0;
}
@@ -690,7 +700,7 @@ public class KeyWritableRepository extends KeyRepository {
// first, mark all keys as not available
ContentValues values = new ContentValues();
values.put(Keys.HAS_SECRET, SecretKeyType.GNU_DUMMY.getNum());
mContentResolver.update(uri, values, null, null);
contentResolver.update(uri, values, null, null);
// then, mark exactly the keys we have available
log(LogType.MSG_IS_IMPORTING_SUBKEYS);
@@ -699,7 +709,7 @@ public class KeyWritableRepository extends KeyRepository {
long id = sub.getKeyId();
SecretKeyType mode = sub.getSecretKeyTypeSuperExpensive();
values.put(Keys.HAS_SECRET, mode.getNum());
int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?",
int upd = contentResolver.update(uri, values, Keys.KEY_ID + " = ?",
new String[]{Long.toString(id)});
if (upd == 1) {
switch (mode) {
@@ -1030,11 +1040,11 @@ public class KeyWritableRepository extends KeyRepository {
log.add(LogType.MSG_TRUST, 0);
Cursor cursor;
Preferences preferences = Preferences.getPreferences(mContext);
Preferences preferences = Preferences.getPreferences(context);
boolean isTrustDbInitialized = preferences.isKeySignaturesTableInitialized();
if (!isTrustDbInitialized) {
log.add(LogType.MSG_TRUST_INITIALIZE, 1);
cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(),
cursor = contentResolver.query(KeyRings.buildUnifiedKeyRingsUri(),
new String[] { KeyRings.MASTER_KEY_ID }, null, null, null);
} else {
String[] signerMasterKeyIdStrings = new String[signerMasterKeyIds.size()];
@@ -1044,7 +1054,7 @@ public class KeyWritableRepository extends KeyRepository {
signerMasterKeyIdStrings[i++] = Long.toString(masterKeyId);
}
cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsFilterBySigner(),
cursor = contentResolver.query(KeyRings.buildUnifiedKeyRingsFilterBySigner(),
new String[] { KeyRings.MASTER_KEY_ID }, null, signerMasterKeyIdStrings, null);
}

View File

@@ -50,6 +50,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.DatabaseUtil;
import timber.log.Timber;
import static android.database.DatabaseUtils.dumpCursorToString;
@@ -101,7 +102,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
String authority = KeychainContract.CONTENT_AUTHORITY;
/**
/*
* list key_rings
*
* <pre>
@@ -124,7 +125,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
+ "/" + KeychainContract.PATH_USER_IDS,
KEY_RINGS_USER_IDS);
/**
/*
* find by criteria other than master key id
*
* key_rings/find/email/_
@@ -144,7 +145,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
+ KeychainContract.PATH_FILTER + "/" + KeychainContract.PATH_BY_SIGNER,
KEY_RINGS_FILTER_BY_SIGNER);
/**
/*
* list key_ring specifics
*
* <pre>
@@ -189,7 +190,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
+ KeychainContract.PATH_CERTS + "/*/*",
KEY_RING_CERTS_SPECIFIC);
/**
/*
* API apps
*
* <pre>
@@ -205,7 +206,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ KeychainContract.PATH_ALLOWED_KEYS, API_ALLOWED_KEYS);
/**
/*
* Trust Identity access
*
* <pre>
@@ -221,7 +222,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
KeychainContract.PATH_BY_PACKAGE_NAME + "/*/*", AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID);
/**
/*
* to access table containing last updated dates of keys
*/
matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS, UPDATED_KEYS);
@@ -835,7 +836,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
SQLiteDatabase db = getDb().getReadableDatabase();
Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy);
Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, null, orderBy);
if (cursor != null) {
// Tell the cursor what uri to watch, so it knows when its source data changes
cursor.setNotificationUri(getContext().getContentResolver(), uri);
@@ -849,27 +850,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
if (Constants.DEBUG && Constants.DEBUG_EXPLAIN_QUERIES) {
String rawQuery = qb.buildQuery(projection, selection, groupBy, having, orderBy, null);
Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + rawQuery, selectionArgs);
// this is a debugging feature, we can be a little careless
explainCursor.moveToFirst();
StringBuilder line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getColumnName(i)).append(", ");
}
Timber.d(line.toString());
while (!explainCursor.isAfterLast()) {
line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getString(i)).append(", ");
}
Timber.d(line.toString());
explainCursor.moveToNext();
}
explainCursor.close();
DatabaseUtil.explainQuery(db, rawQuery);
}
return cursor;
@@ -966,9 +947,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
rowUri = uri;
}
// notify of changes in db
getContext().getContentResolver().notifyChange(uri, null);
} catch (SQLiteConstraintException e) {
Timber.d(e, "Constraint exception on insert! Entry already existing?");
}
@@ -1003,7 +981,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
}
// corresponding keys and userIds are deleted by ON DELETE CASCADE
count = db.delete(Tables.KEY_RINGS_PUBLIC, selection, selectionArgs);
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1)), null);
break;
}
case KEY_RING_SECRET: {
@@ -1013,7 +990,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
selection += " AND (" + additionalSelection + ")";
}
count = db.delete(Tables.KEY_RINGS_SECRET, selection, selectionArgs);
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1)), null);
break;
}
@@ -1024,20 +1000,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
String selection = ApiAutocryptPeer.PACKAGE_NAME + " = ? AND " + ApiAutocryptPeer.IDENTIFIER + " = ?";
selectionArgs = new String[] { packageName, autocryptPeer };
Cursor cursor = db.query(Tables.API_AUTOCRYPT_PEERS, new String[] { ApiAutocryptPeer.MASTER_KEY_ID },
selection, selectionArgs, null, null, null);
Long masterKeyId = null;
if (cursor != null && cursor.moveToNext() && !cursor.isNull(0)) {
masterKeyId = cursor.getLong(0);
}
count = db.delete(Tables.API_AUTOCRYPT_PEERS, selection, selectionArgs);
if (masterKeyId != null) {
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(masterKeyId), null);
}
contentResolver.notifyChange(
ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptPeer), null);
break;
}
@@ -1047,7 +1010,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
selection += " AND (" + additionalSelection + ")";
}
count = db.delete(Tables.API_AUTOCRYPT_PEERS, selection, selectionArgs);
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(uri.getLastPathSegment()), null);
break;
case API_APPS_BY_PACKAGE_NAME: {
@@ -1076,7 +1038,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
Timber.v("update(uri=" + uri + ", values=" + values.toString() + ")");
final SQLiteDatabase db = getDb().getWritableDatabase();
ContentResolver contentResolver = getContext().getContentResolver();
int count = 0;
try {
@@ -1127,25 +1088,12 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
db.insertOrThrow(Tables.API_AUTOCRYPT_PEERS, null, values);
}
Long masterKeyId = values.getAsLong(ApiAutocryptPeer.MASTER_KEY_ID);
if (masterKeyId != null) {
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(masterKeyId), null);
}
Long gossipMasterKeyId = values.getAsLong(ApiAutocryptPeer.GOSSIP_MASTER_KEY_ID);
if (gossipMasterKeyId != null && !gossipMasterKeyId.equals(masterKeyId)) {
contentResolver.notifyChange(KeyRings.buildGenericKeyRingUri(gossipMasterKeyId), null);
}
break;
}
default: {
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
// notify of changes in db
contentResolver.notifyChange(uri, null);
} catch (SQLiteConstraintException e) {
Timber.d(e, "Constraint exception on update! Entry already existing?");
}

View File

@@ -15,14 +15,16 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys;
public class LastUpdateInteractor {
private final ContentResolver contentResolver;
private final DatabaseNotifyManager databaseNotifyManager;
public static LastUpdateInteractor create(Context context) {
return new LastUpdateInteractor(context.getContentResolver());
return new LastUpdateInteractor(context.getContentResolver(), DatabaseNotifyManager.create(context));
}
private LastUpdateInteractor(ContentResolver contentResolver) {
private LastUpdateInteractor(ContentResolver contentResolver, DatabaseNotifyManager databaseNotifyManager) {
this.contentResolver = contentResolver;
this.databaseNotifyManager = databaseNotifyManager;
}
@Nullable
@@ -69,6 +71,8 @@ public class LastUpdateInteractor {
// this will actually update/replace, doing the right thing™ for seenOnKeyservers value
// see `KeychainProvider.insert()`
return contentResolver.insert(UpdatedKeys.CONTENT_URI, values);
Uri insert = contentResolver.insert(UpdatedKeys.CONTENT_URI, values);
databaseNotifyManager.notifyKeyserverStatusChange(masterKeyId);
return insert;
}
}

View File

@@ -25,6 +25,7 @@ import java.util.List;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.DatabaseUtils;
@@ -40,6 +41,7 @@ import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptRecommendationResult;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptState;
import org.sufficientlysecure.keychain.provider.DatabaseNotifyManager;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -72,6 +74,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
private UriMatcher uriMatcher;
private ApiPermissionHelper apiPermissionHelper;
private KeychainProvider internalKeychainProvider;
private DatabaseNotifyManager databaseNotifyManager;
/**
@@ -103,9 +106,15 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
public boolean onCreate() {
uriMatcher = buildUriMatcher();
Context context = getContext();
if (context == null) {
throw new NullPointerException("Context can't be null during onCreate!");
}
internalKeychainProvider = new KeychainProvider();
internalKeychainProvider.attachInfo(getContext(), null);
apiPermissionHelper = new ApiPermissionHelper(getContext(), new ApiDataAccessObject(internalKeychainProvider));
internalKeychainProvider.attachInfo(context, null);
apiPermissionHelper = new ApiPermissionHelper(context, new ApiDataAccessObject(internalKeychainProvider));
databaseNotifyManager = DatabaseNotifyManager.create(context);
return true;
}
@@ -270,7 +279,8 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
}
if (!isWildcardSelector && queriesAutocryptResult) {
AutocryptPeerDataAccessObject autocryptPeerDao =
new AutocryptPeerDataAccessObject(internalKeychainProvider, callingPackageName);
new AutocryptPeerDataAccessObject(internalKeychainProvider, callingPackageName,
databaseNotifyManager);
fillTempTableWithAutocryptRecommendations(db, autocryptPeerDao, selectionArgs);
}

View File

@@ -37,10 +37,10 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.linked.UriAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter.ViewHolder;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.AutocryptPeerInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.UserIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.UserIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.AutocryptPeerInfo;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker;

View File

@@ -18,6 +18,11 @@
package org.sufficientlysecure.keychain.ui.keyview;
import java.util.List;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
@@ -36,6 +41,10 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.ui.base.LoaderFragment;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusDao.KeyserverStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
import org.sufficientlysecure.keychain.ui.keyview.presenter.IdentitiesPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyHealthPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyserverStatusPresenter;
@@ -53,11 +62,6 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, O
boolean mIsSecret = false;
private static final int LOADER_IDENTITIES = 1;
private static final int LOADER_ID_LINKED_CONTACT = 2;
private static final int LOADER_ID_SUBKEY_STATUS = 3;
private static final int LOADER_ID_KEYSERVER_STATUS = 4;
private IdentitiesCardView identitiesCardView;
private IdentitiesPresenter identitiesPresenter;
@@ -100,6 +104,41 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, O
return root;
}
public static class KeyFragmentViewModel extends ViewModel {
private LiveData<List<IdentityInfo>> identityInfo;
private LiveData<KeySubkeyStatus> subkeyStatus;
private LiveData<SystemContactInfo> systemContactInfo;
private LiveData<KeyserverStatus> keyserverStatus;
LiveData<List<IdentityInfo>> getIdentityInfo(IdentitiesPresenter identitiesPresenter) {
if (identityInfo == null) {
identityInfo = identitiesPresenter.getLiveDataInstance();
}
return identityInfo;
}
LiveData<KeySubkeyStatus> getSubkeyStatus(KeyHealthPresenter keyHealthPresenter) {
if (subkeyStatus == null) {
subkeyStatus = keyHealthPresenter.getLiveDataInstance();
}
return subkeyStatus;
}
LiveData<SystemContactInfo> getSystemContactInfo(SystemContactPresenter systemContactPresenter) {
if (systemContactInfo == null) {
systemContactInfo = systemContactPresenter.getLiveDataInstance();
}
return systemContactInfo;
}
LiveData<KeyserverStatus> getKeyserverStatus(KeyserverStatusPresenter keyserverStatusPresenter) {
if (keyserverStatus == null) {
keyserverStatus = keyserverStatusPresenter.getLiveDataInstance();
}
return keyserverStatus;
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@@ -107,21 +146,22 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, O
long masterKeyId = getArguments().getLong(ARG_MASTER_KEY_ID);
mIsSecret = getArguments().getBoolean(ARG_IS_SECRET);
KeyFragmentViewModel model = ViewModelProviders.of(this).get(KeyFragmentViewModel.class);
identitiesPresenter = new IdentitiesPresenter(
getContext(), identitiesCardView, this, LOADER_IDENTITIES, masterKeyId, mIsSecret);
identitiesPresenter.startLoader(getLoaderManager());
getContext(), identitiesCardView, this, masterKeyId, mIsSecret);
model.getIdentityInfo(identitiesPresenter).observe(this, identitiesPresenter);
systemContactPresenter = new SystemContactPresenter(
getContext(), systemContactCard, LOADER_ID_LINKED_CONTACT, masterKeyId, mIsSecret);
systemContactPresenter.startLoader(getLoaderManager());
getContext(), systemContactCard, masterKeyId, mIsSecret);
model.getSystemContactInfo(systemContactPresenter).observe(this, systemContactPresenter);
keyHealthPresenter = new KeyHealthPresenter(
getContext(), keyStatusHealth, LOADER_ID_SUBKEY_STATUS, masterKeyId, mIsSecret);
keyHealthPresenter.startLoader(getLoaderManager());
keyHealthPresenter = new KeyHealthPresenter(getContext(), keyStatusHealth, masterKeyId);
model.getSubkeyStatus(keyHealthPresenter).observe(this, keyHealthPresenter);
keyserverStatusPresenter = new KeyserverStatusPresenter(
getContext(), keyStatusKeyserver, LOADER_ID_KEYSERVER_STATUS, masterKeyId, mIsSecret);
keyserverStatusPresenter.startLoader(getLoaderManager());
getContext(), keyStatusKeyserver, masterKeyId, mIsSecret);
model.getKeyserverStatus(keyserverStatusPresenter).observe(this, keyserverStatusPresenter);
}
@Override

View File

@@ -26,11 +26,11 @@ import java.util.List;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v4.content.AsyncTaskLoader;
import com.google.auto.value.AutoValue;
import org.openintents.openpgp.util.OpenPgpApi;
@@ -38,14 +38,12 @@ import org.sufficientlysecure.keychain.linked.LinkedAttribute;
import org.sufficientlysecure.keychain.linked.UriAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeer;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.IdentityInfo;
import org.sufficientlysecure.keychain.ui.util.PackageIconGetter;
import timber.log.Timber;
public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
public class IdentityDao {
private static final String[] USER_PACKETS_PROJECTION = new String[]{
UserPackets._ID,
UserPackets.TYPE,
@@ -84,41 +82,34 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
private final ContentResolver contentResolver;
private final PackageIconGetter packageIconGetter;
private final long masterKeyId;
private final boolean showLinkedIds;
private final PackageManager packageManager;
private List<IdentityInfo> cachedResult;
private ForceLoadContentObserver identityObserver;
public IdentityLoader(Context context, ContentResolver contentResolver, long masterKeyId, boolean showLinkedIds) {
super(context);
this.contentResolver = contentResolver;
this.masterKeyId = masterKeyId;
this.showLinkedIds = showLinkedIds;
this.identityObserver = new ForceLoadContentObserver();
this.packageIconGetter = PackageIconGetter.getInstance(context);
this.identityObserver = new ForceLoadContentObserver();
static IdentityDao getInstance(Context context) {
ContentResolver contentResolver = context.getContentResolver();
PackageManager packageManager = context.getPackageManager();
PackageIconGetter iconGetter = PackageIconGetter.getInstance(context);
return new IdentityDao(contentResolver, packageManager, iconGetter);
}
@Override
public List<IdentityInfo> loadInBackground() {
private IdentityDao(ContentResolver contentResolver, PackageManager packageManager, PackageIconGetter iconGetter) {
this.packageManager = packageManager;
this.contentResolver = contentResolver;
this.packageIconGetter = iconGetter;
}
List<IdentityInfo> getIdentityInfos(long masterKeyId, boolean showLinkedIds) {
ArrayList<IdentityInfo> identities = new ArrayList<>();
if (showLinkedIds) {
loadLinkedIds(identities);
loadLinkedIds(identities, masterKeyId);
}
loadUserIds(identities);
correlateOrAddAutocryptPeers(identities);
loadUserIds(identities, masterKeyId);
correlateOrAddAutocryptPeers(identities, masterKeyId);
return Collections.unmodifiableList(identities);
}
private void correlateOrAddAutocryptPeers(ArrayList<IdentityInfo> identities) {
private void correlateOrAddAutocryptPeers(ArrayList<IdentityInfo> identities, long masterKeyId) {
Cursor cursor = contentResolver.query(ApiAutocryptPeer.buildByMasterKeyId(masterKeyId),
AUTOCRYPT_PEER_PROJECTION, null, null, null);
if (cursor == null) {
@@ -158,7 +149,7 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID, autocryptPeer);
List<ResolveInfo> resolveInfos = getContext().getPackageManager().queryIntentActivities(intent, 0);
List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, 0);
if (resolveInfos != null && !resolveInfos.isEmpty()) {
return intent;
} else {
@@ -178,7 +169,7 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
return null;
}
private void loadLinkedIds(ArrayList<IdentityInfo> identities) {
private void loadLinkedIds(ArrayList<IdentityInfo> identities, long masterKeyId) {
Cursor cursor = contentResolver.query(UserPackets.buildLinkedIdsUri(masterKeyId),
USER_PACKETS_PROJECTION, USER_IDS_WHERE, null, null);
if (cursor == null) {
@@ -208,7 +199,7 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
}
}
private void loadUserIds(ArrayList<IdentityInfo> identities) {
private void loadUserIds(ArrayList<IdentityInfo> identities, long masterKeyId) {
Cursor cursor = contentResolver.query(UserPackets.buildUserIdsUri(masterKeyId),
USER_PACKETS_PROJECTION, USER_IDS_WHERE, null, null);
if (cursor == null) {
@@ -236,36 +227,6 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
}
}
@Override
public void deliverResult(List<IdentityInfo> keySubkeyStatus) {
cachedResult = keySubkeyStatus;
if (isStarted()) {
super.deliverResult(keySubkeyStatus);
}
}
@Override
protected void onStartLoading() {
if (cachedResult != null) {
deliverResult(cachedResult);
}
if (takeContentChanged() || cachedResult == null) {
forceLoad();
}
getContext().getContentResolver().registerContentObserver(
KeyRings.buildGenericKeyRingUri(masterKeyId), true, identityObserver);
}
@Override
protected void onAbandon() {
super.onAbandon();
getContext().getContentResolver().unregisterContentObserver(identityObserver);
}
public interface IdentityInfo {
int getRank();
int getVerified();
@@ -287,7 +248,7 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
static UserIdInfo create(int rank, int verified, boolean isPrimary, String name, String email,
String comment) {
return new AutoValue_IdentityLoader_UserIdInfo(rank, verified, isPrimary, name, email, comment);
return new AutoValue_IdentityDao_UserIdInfo(rank, verified, isPrimary, name, email, comment);
}
}
@@ -300,7 +261,7 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
public abstract UriAttribute getUriAttribute();
static LinkedIdInfo create(int rank, int verified, boolean isPrimary, UriAttribute uriAttribute) {
return new AutoValue_IdentityLoader_LinkedIdInfo(rank, verified, isPrimary, uriAttribute);
return new AutoValue_IdentityDao_LinkedIdInfo(rank, verified, isPrimary, uriAttribute);
}
}
@@ -321,12 +282,12 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
static AutocryptPeerInfo create(UserIdInfo userIdInfo, String autocryptPeer, String packageName,
Drawable appIcon, Intent autocryptPeerIntent) {
return new AutoValue_IdentityLoader_AutocryptPeerInfo(userIdInfo.getRank(), userIdInfo.getVerified(),
return new AutoValue_IdentityDao_AutocryptPeerInfo(userIdInfo.getRank(), userIdInfo.getVerified(),
userIdInfo.isPrimary(), autocryptPeer, packageName, appIcon, userIdInfo, autocryptPeerIntent);
}
static AutocryptPeerInfo create(String autocryptPeer, String packageName, Drawable appIcon, Intent autocryptPeerIntent) {
return new AutoValue_IdentityLoader_AutocryptPeerInfo(
return new AutoValue_IdentityDao_AutocryptPeerInfo(
0, Certs.VERIFIED_SELF, false, autocryptPeer, packageName, appIcon, null, autocryptPeerIntent);
}
}

View File

@@ -23,17 +23,12 @@ import java.util.Date;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.content.AsyncTaskLoader;
import android.util.Log;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys;
import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusLoader.KeyserverStatus;
import timber.log.Timber;
public class KeyserverStatusLoader extends AsyncTaskLoader<KeyserverStatus> {
public class KeyserverStatusDao {
public static final String[] PROJECTION = new String[] {
UpdatedKeys.LAST_UPDATED,
UpdatedKeys.SEEN_ON_KEYSERVERS
@@ -43,23 +38,17 @@ public class KeyserverStatusLoader extends AsyncTaskLoader<KeyserverStatus> {
private final ContentResolver contentResolver;
private final long masterKeyId;
private KeyserverStatus cachedResult;
private ForceLoadContentObserver keyserverStatusObserver;
public KeyserverStatusLoader(Context context, ContentResolver contentResolver, long masterKeyId) {
super(context);
this.contentResolver = contentResolver;
this.masterKeyId = masterKeyId;
this.keyserverStatusObserver = new ForceLoadContentObserver();
public static KeyserverStatusDao getInstance(Context context) {
ContentResolver contentResolver = context.getContentResolver();
return new KeyserverStatusDao(contentResolver);
}
@Override
public KeyserverStatus loadInBackground() {
private KeyserverStatusDao(ContentResolver contentResolver) {
this.contentResolver = contentResolver;
}
public KeyserverStatus getKeyserverStatus(long masterKeyId) {
Cursor cursor = contentResolver.query(UpdatedKeys.CONTENT_URI, PROJECTION,
UpdatedKeys.MASTER_KEY_ID + " = ?", new String[] { Long.toString(masterKeyId) }, null);
if (cursor == null) {
@@ -85,36 +74,6 @@ public class KeyserverStatusLoader extends AsyncTaskLoader<KeyserverStatus> {
}
}
@Override
public void deliverResult(KeyserverStatus keySubkeyStatus) {
cachedResult = keySubkeyStatus;
if (isStarted()) {
super.deliverResult(keySubkeyStatus);
}
}
@Override
protected void onStartLoading() {
if (cachedResult != null) {
deliverResult(cachedResult);
}
if (takeContentChanged() || cachedResult == null) {
forceLoad();
}
getContext().getContentResolver().registerContentObserver(
KeyRings.buildGenericKeyRingUri(masterKeyId), true, keyserverStatusObserver);
}
@Override
protected void onAbandon() {
super.onAbandon();
getContext().getContentResolver().unregisterContentObserver(keyserverStatusObserver);
}
public static class KeyserverStatus {
private final long masterKeyId;
private final boolean isPublished;

View File

@@ -28,19 +28,15 @@ import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.v4.content.AsyncTaskLoader;
import android.util.Log;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusLoader.KeySubkeyStatus;
import timber.log.Timber;
public class SubkeyStatusLoader extends AsyncTaskLoader<KeySubkeyStatus> {
public class SubkeyStatusDao {
public static final String[] PROJECTION = new String[] {
Keys.KEY_ID,
Keys.CREATION,
@@ -68,23 +64,18 @@ public class SubkeyStatusLoader extends AsyncTaskLoader<KeySubkeyStatus> {
private final ContentResolver contentResolver;
private final long masterKeyId;
private final Comparator<SubKeyItem> comparator;
private KeySubkeyStatus cachedResult;
public SubkeyStatusLoader(Context context, ContentResolver contentResolver, long masterKeyId,
Comparator<SubKeyItem> comparator) {
super(context);
this.contentResolver = contentResolver;
this.masterKeyId = masterKeyId;
this.comparator = comparator;
public static SubkeyStatusDao getInstance(Context context) {
ContentResolver contentResolver = context.getContentResolver();
return new SubkeyStatusDao(contentResolver);
}
@Override
public KeySubkeyStatus loadInBackground() {
private SubkeyStatusDao(ContentResolver contentResolver) {
this.contentResolver = contentResolver;
}
KeySubkeyStatus getSubkeyStatus(long masterKeyId, Comparator<SubKeyItem> comparator) {
Cursor cursor = contentResolver.query(Keys.buildKeysUri(masterKeyId), PROJECTION, null, null, null);
if (cursor == null) {
Timber.e("Error loading key items!");
@@ -111,7 +102,10 @@ public class SubkeyStatusLoader extends AsyncTaskLoader<KeySubkeyStatus> {
}
if (keyCertify == null) {
throw new IllegalStateException("Certification key must be set at this point, it's a bug otherwise!");
if (!keysSign.isEmpty() || !keysEncrypt.isEmpty()) {
throw new IllegalStateException("Certification key can't be missing for a key that hasn't been deleted!");
}
return null;
}
Collections.sort(keysSign, comparator);
@@ -123,26 +117,6 @@ public class SubkeyStatusLoader extends AsyncTaskLoader<KeySubkeyStatus> {
}
}
@Override
public void deliverResult(KeySubkeyStatus keySubkeyStatus) {
cachedResult = keySubkeyStatus;
if (isStarted()) {
super.deliverResult(keySubkeyStatus);
}
}
@Override
protected void onStartLoading() {
if (cachedResult != null) {
deliverResult(cachedResult);
}
if (takeContentChanged() || cachedResult == null) {
forceLoad();
}
}
public static class KeySubkeyStatus {
@NonNull
public final SubKeyItem keyCertify;

View File

@@ -20,22 +20,22 @@ package org.sufficientlysecure.keychain.ui.keyview.loader;
import java.util.List;
import android.Manifest;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.ContactsContract;
import android.support.v4.content.AsyncTaskLoader;
import android.util.Log;
import android.support.v4.content.ContextCompat;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactInfoLoader.SystemContactInfo;
import org.sufficientlysecure.keychain.util.ContactHelper;
import timber.log.Timber;
public class SystemContactInfoLoader extends AsyncTaskLoader<SystemContactInfo> {
public class SystemContactDao {
private static final String[] PROJECTION = {
ContactsContract.RawContacts.CONTACT_ID
};
@@ -43,23 +43,30 @@ public class SystemContactInfoLoader extends AsyncTaskLoader<SystemContactInfo>
private static final String CONTACT_NOT_DELETED = "0";
private final Context context;
private final ContentResolver contentResolver;
private final long masterKeyId;
private final boolean isSecret;
private SystemContactInfo cachedResult;
private final ContactHelper contactHelper;
public SystemContactInfoLoader(Context context, ContentResolver contentResolver, long masterKeyId, boolean isSecret) {
super(context);
this.contentResolver = contentResolver;
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
public static SystemContactDao getInstance(Context context) {
ContactHelper contactHelper = new ContactHelper(context);
ContentResolver contentResolver = context.getContentResolver();
return new SystemContactDao(context, contactHelper, contentResolver);
}
@Override
public SystemContactInfo loadInBackground() {
private SystemContactDao(Context context, ContactHelper contactHelper, ContentResolver contentResolver) {
this.context = context;
this.contactHelper = contactHelper;
this.contentResolver = contentResolver;
}
SystemContactInfo getSystemContactInfo(long masterKeyId, boolean isSecret) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
== PackageManager.PERMISSION_DENIED) {
Timber.w(Constants.TAG, "loading linked system contact not possible READ_CONTACTS permission denied!");
return null;
}
Uri baseUri = isSecret ? ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI :
ContactsContract.RawContacts.CONTENT_URI;
Cursor cursor = contentResolver.query(baseUri, PROJECTION,
@@ -87,8 +94,6 @@ public class SystemContactInfoLoader extends AsyncTaskLoader<SystemContactInfo>
return null;
}
ContactHelper contactHelper = new ContactHelper(getContext());
String contactName = null;
if (isSecret) { //all secret keys are linked to "me" profile in contacts
List<String> mainProfileNames = contactHelper.getMainProfileContactName();
@@ -116,26 +121,6 @@ public class SystemContactInfoLoader extends AsyncTaskLoader<SystemContactInfo>
}
}
@Override
public void deliverResult(SystemContactInfo systemContactInfo) {
cachedResult = systemContactInfo;
if (isStarted()) {
super.deliverResult(systemContactInfo);
}
}
@Override
protected void onStartLoading() {
if (cachedResult != null) {
deliverResult(cachedResult);
}
if (takeContentChanged() || cachedResult == null) {
forceLoad();
}
}
public static class SystemContactInfo {
final long masterKeyId;
public final long contactId;

View File

@@ -0,0 +1,98 @@
package org.sufficientlysecure.keychain.ui.keyview.loader;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusDao.KeyserverStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.SubKeyItem;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
public class ViewKeyLiveData {
public static class IdentityLiveData extends AsyncTaskLiveData<List<IdentityInfo>> {
private final IdentityDao identityDao;
private final long masterKeyId;
private final boolean showLinkedIds;
public IdentityLiveData(Context context, long masterKeyId, boolean showLinkedIds) {
super(context, KeyRings.buildGenericKeyRingUri(masterKeyId));
this.identityDao = IdentityDao.getInstance(context);
this.masterKeyId = masterKeyId;
this.showLinkedIds = showLinkedIds;
}
@Override
public List<IdentityInfo> asyncLoadData() {
return identityDao.getIdentityInfos(masterKeyId, showLinkedIds);
}
}
public static class SubkeyStatusLiveData extends AsyncTaskLiveData<KeySubkeyStatus> {
private final SubkeyStatusDao subkeyStatusDao;
private final long masterKeyId;
private final Comparator<SubKeyItem> comparator;
public SubkeyStatusLiveData(Context context, long masterKeyId, Comparator<SubKeyItem> comparator) {
super(context, KeyRings.buildGenericKeyRingUri(masterKeyId));
this.subkeyStatusDao = SubkeyStatusDao.getInstance(context);
this.masterKeyId = masterKeyId;
this.comparator = comparator;
}
@Override
public KeySubkeyStatus asyncLoadData() {
return subkeyStatusDao.getSubkeyStatus(masterKeyId, comparator);
}
}
public static class SystemContactInfoLiveData extends AsyncTaskLiveData<SystemContactInfo> {
private final SystemContactDao systemContactDao;
private final long masterKeyId;
private final boolean isSecret;
public SystemContactInfoLiveData(Context context, long masterKeyId, boolean isSecret) {
super(context, null);
this.systemContactDao = SystemContactDao.getInstance(context);
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
}
@Override
public SystemContactInfo asyncLoadData() {
return systemContactDao.getSystemContactInfo(masterKeyId, isSecret);
}
}
public static class KeyserverStatusLiveData extends AsyncTaskLiveData<KeyserverStatus> {
private final KeyserverStatusDao keyserverStatusDao;
private final long masterKeyId;
public KeyserverStatusLiveData(Context context, long masterKeyId) {
super(context, KeyRings.buildGenericKeyRingUri(masterKeyId));
this.keyserverStatusDao = KeyserverStatusDao.getInstance(context);
this.masterKeyId = masterKeyId;
}
@Override
public KeyserverStatus asyncLoadData() {
return keyserverStatusDao.getKeyserverStatus(masterKeyId);
}
}
}

View File

@@ -21,13 +21,12 @@ package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.io.IOException;
import java.util.List;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.annotation.Nullable;
import android.view.View;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject;
@@ -37,21 +36,20 @@ import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter.IdentityClickListener;
import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
import org.sufficientlysecure.keychain.ui.keyview.LinkedIdViewFragment;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.AutocryptPeerInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.UserIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.AutocryptPeerInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.UserIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.ViewKeyLiveData.IdentityLiveData;
import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard;
import org.sufficientlysecure.keychain.util.Preferences;
import timber.log.Timber;
public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>> {
public class IdentitiesPresenter implements Observer<List<IdentityInfo>> {
private final Context context;
private final IdentitiesMvpView view;
private final ViewKeyMvpView viewKeyMvpView;
private final int loaderId;
private final IdentityAdapter identitiesAdapter;
@@ -60,11 +58,10 @@ public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>>
private final boolean showLinkedIds;
public IdentitiesPresenter(Context context, IdentitiesMvpView view, ViewKeyMvpView viewKeyMvpView,
int loaderId, long masterKeyId, boolean isSecret) {
long masterKeyId, boolean isSecret) {
this.context = context;
this.view = view;
this.viewKeyMvpView = viewKeyMvpView;
this.loaderId = loaderId;
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
@@ -90,24 +87,10 @@ public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>>
view.setIdentitiesCardListener(() -> addLinkedIdentity());
}
public void startLoader(LoaderManager loaderManager) {
loaderManager.restartLoader(loaderId, null, this);
}
@Override
public Loader<List<IdentityInfo>> onCreateLoader(int id, Bundle args) {
return new IdentityLoader(context, context.getContentResolver(), masterKeyId, showLinkedIds);
}
@Override
public void onLoadFinished(Loader<List<IdentityInfo>> loader, List<IdentityInfo> data) {
public void onChanged(@Nullable List<IdentityInfo> identityInfos) {
viewKeyMvpView.setContentShown(true, false);
identitiesAdapter.setData(data);
}
@Override
public void onLoaderReset(Loader loader) {
identitiesAdapter.setData(null);
identitiesAdapter.setData(identityInfos);
}
private void showIdentityInfo(final int position) {
@@ -168,6 +151,10 @@ public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>>
autocryptPeerDao.delete(info.getIdentity());
}
public LiveData<List<IdentityInfo>> getLiveDataInstance() {
return new IdentityLiveData(context, masterKeyId, showLinkedIds);
}
public interface IdentitiesMvpView {
void setIdentitiesAdapter(IdentityAdapter userIdsAdapter);
void setIdentitiesCardListener(IdentitiesCardListener identitiesCardListener);

View File

@@ -21,21 +21,21 @@ 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;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.SubKeyItem;
import org.sufficientlysecure.keychain.ui.keyview.loader.ViewKeyLiveData.SubkeyStatusLiveData;
import org.sufficientlysecure.keychain.ui.keyview.view.KeyStatusList.KeyDisplayStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusLoader;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusLoader.KeySubkeyStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusLoader.SubKeyItem;
public class KeyHealthPresenter implements LoaderCallbacks<KeySubkeyStatus> {
public class KeyHealthPresenter implements Observer<KeySubkeyStatus> {
private static final Comparator<SubKeyItem> SUBKEY_COMPARATOR = new Comparator<SubKeyItem>() {
@Override
public int compare(SubKeyItem one, SubKeyItem two) {
@@ -58,22 +58,16 @@ public class KeyHealthPresenter implements LoaderCallbacks<KeySubkeyStatus> {
private final Context context;
private final KeyHealthMvpView view;
private final int loaderId;
private final long masterKeyId;
private final boolean isSecret;
private KeySubkeyStatus subkeyStatus;
private boolean showingExpandedInfo;
public KeyHealthPresenter(Context context, KeyHealthMvpView view, int loaderId, long masterKeyId, boolean isSecret) {
public KeyHealthPresenter(Context context, KeyHealthMvpView view, long masterKeyId) {
this.context = context;
this.view = view;
this.loaderId = loaderId;
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
view.setOnHealthClickListener(new KeyHealthClickListener() {
@Override
@@ -83,18 +77,12 @@ public class KeyHealthPresenter implements LoaderCallbacks<KeySubkeyStatus> {
});
}
public void startLoader(LoaderManager loaderManager) {
loaderManager.restartLoader(loaderId, null, this);
}
@Override
public Loader<KeySubkeyStatus> onCreateLoader(int id, Bundle args) {
return new SubkeyStatusLoader(context, context.getContentResolver(), masterKeyId, SUBKEY_COMPARATOR);
}
@Override
public void onLoadFinished(Loader<KeySubkeyStatus> loader, KeySubkeyStatus subkeyStatus) {
public void onChanged(@Nullable KeySubkeyStatus subkeyStatus) {
this.subkeyStatus = subkeyStatus;
if (subkeyStatus == null) {
return;
}
KeyHealthStatus keyHealthStatus = determineKeyHealthStatus(subkeyStatus);
@@ -195,11 +183,6 @@ public class KeyHealthPresenter implements LoaderCallbacks<KeySubkeyStatus> {
return KeyHealthStatus.OK;
}
@Override
public void onLoaderReset(Loader loader) {
}
private void onKeyHealthClick() {
if (showingExpandedInfo) {
showingExpandedInfo = false;
@@ -262,6 +245,10 @@ public class KeyHealthPresenter implements LoaderCallbacks<KeySubkeyStatus> {
return KeyDisplayStatus.OK;
}
public LiveData<KeySubkeyStatus> getLiveDataInstance() {
return new SubkeyStatusLiveData(context, masterKeyId, SUBKEY_COMPARATOR);
}
public enum KeyHealthStatus {
OK, DIVERT, DIVERT_PARTIAL, REVOKED, EXPIRED, INSECURE, SIGN_ONLY, STRIPPED, PARTIAL_STRIPPED, BROKEN
}

View File

@@ -20,46 +20,43 @@ package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.util.Date;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusLoader;
import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusLoader.KeyserverStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusDao.KeyserverStatus;
import org.sufficientlysecure.keychain.ui.keyview.loader.ViewKeyLiveData.KeyserverStatusLiveData;
public class KeyserverStatusPresenter implements LoaderCallbacks<KeyserverStatus> {
public class KeyserverStatusPresenter implements Observer<KeyserverStatus> {
private final Context context;
private final KeyserverStatusMvpView view;
private final int loaderId;
private final long masterKeyId;
private final boolean isSecret;
public KeyserverStatusPresenter(Context context, KeyserverStatusMvpView view, int loaderId, long masterKeyId,
public KeyserverStatusPresenter(Context context, KeyserverStatusMvpView view, long masterKeyId,
boolean isSecret) {
this.context = context;
this.view = view;
this.loaderId = loaderId;
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
}
public void startLoader(LoaderManager loaderManager) {
loaderManager.restartLoader(loaderId, null, this);
public LiveData<KeyserverStatus> getLiveDataInstance() {
return new KeyserverStatusLiveData(context, masterKeyId);
}
@Override
public Loader<KeyserverStatus> onCreateLoader(int id, Bundle args) {
return new KeyserverStatusLoader(context, context.getContentResolver(), masterKeyId);
}
public void onChanged(@Nullable KeyserverStatus keyserverStatus) {
if (keyserverStatus == null) {
view.setDisplayStatusUnknown();
return;
}
@Override
public void onLoadFinished(Loader<KeyserverStatus> loader, KeyserverStatus keyserverStatus) {
if (keyserverStatus.hasBeenUpdated()) {
if (keyserverStatus.isPublished()) {
view.setDisplayStatusPublished();
@@ -72,11 +69,6 @@ public class KeyserverStatusPresenter implements LoaderCallbacks<KeyserverStatus
}
}
@Override
public void onLoaderReset(Loader loader) {
}
public interface KeyserverStatusMvpView {
void setDisplayStatusPublished();
void setDisplayStatusNotPublished();

View File

@@ -18,28 +18,22 @@
package org.sufficientlysecure.keychain.ui.keyview.presenter;
import android.Manifest;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.Loader;
import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactInfoLoader;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactInfoLoader.SystemContactInfo;
import timber.log.Timber;
import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.ViewKeyLiveData.SystemContactInfoLiveData;
public class SystemContactPresenter implements LoaderCallbacks<SystemContactInfo> {
public class SystemContactPresenter implements Observer<SystemContactInfo> {
private final Context context;
private final SystemContactMvpView view;
private final int loaderId;
private final long masterKeyId;
private final boolean isSecret;
@@ -47,55 +41,29 @@ public class SystemContactPresenter implements LoaderCallbacks<SystemContactInfo
private long contactId;
public SystemContactPresenter(Context context, SystemContactMvpView view, int loaderId, long masterKeyId, boolean isSecret) {
public SystemContactPresenter(Context context, SystemContactMvpView view, long masterKeyId, boolean isSecret) {
this.context = context;
this.view = view;
this.loaderId = loaderId;
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
view.setSystemContactClickListener(new SystemContactClickListener() {
@Override
public void onSystemContactClick() {
SystemContactPresenter.this.onSystemContactClick();
}
});
view.setSystemContactClickListener(SystemContactPresenter.this::onSystemContactClick);
}
public void startLoader(LoaderManager loaderManager) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
== PackageManager.PERMISSION_DENIED) {
Timber.w("loading linked system contact not possible READ_CONTACTS permission denied!");
public LiveData<SystemContactInfo> getLiveDataInstance() {
return new SystemContactInfoLiveData(context, masterKeyId, isSecret);
}
@Override
public void onChanged(@Nullable SystemContactInfo systemContactInfo) {
if (systemContactInfo == null) {
view.hideLinkedSystemContact();
return;
}
Bundle linkedContactData = new Bundle();
// initialises loader for contact query so we can listen to any updates
loaderManager.restartLoader(loaderId, linkedContactData, this);
}
@Override
public Loader<SystemContactInfo> onCreateLoader(int id, Bundle args) {
return new SystemContactInfoLoader(context, context.getContentResolver(), masterKeyId, isSecret);
}
@Override
public void onLoadFinished(Loader<SystemContactInfo> loader, SystemContactInfo data) {
if (data == null) {
view.hideLinkedSystemContact();
return;
}
this.contactId = data.contactId;
view.showLinkedSystemContact(data.contactName, data.contactPicture);
}
@Override
public void onLoaderReset(Loader loader) {
this.contactId = systemContactInfo.contactId;
view.showLinkedSystemContact(systemContactInfo.contactName, systemContactInfo.contactPicture);
}
private void onSystemContactClick() {

View File

@@ -17,8 +17,13 @@
package org.sufficientlysecure.keychain.util;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import timber.log.Timber;
/**
* Shamelessly copied from android.database.DatabaseUtils
*/
@@ -50,4 +55,28 @@ public class DatabaseUtil {
System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
return result;
}
public static void explainQuery(SQLiteDatabase db, String sql) {
Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + sql, new String[0]);
// this is a debugging feature, we can be a little careless
explainCursor.moveToFirst();
StringBuilder line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getColumnName(i)).append(", ");
}
Timber.d(line.toString());
while (!explainCursor.isAfterLast()) {
line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getString(i)).append(", ");
}
Timber.d(line.toString());
explainCursor.moveToNext();
}
explainCursor.close();
}
}

View File

@@ -1,34 +0,0 @@
package org.sufficientlysecure.keychain.util;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import timber.log.Timber;
public class DatabaseUtils {
public static void explainQuery(SQLiteDatabase db, String sql) {
Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + sql, new String[0]);
// this is a debugging feature, we can be a little careless
explainCursor.moveToFirst();
StringBuilder line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getColumnName(i)).append(", ");
}
Timber.d(line.toString());
while (!explainCursor.isAfterLast()) {
line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getString(i)).append(", ");
}
Timber.d(line.toString());
explainCursor.moveToNext();
}
explainCursor.close();
}
}