From 6cd065a3bd6a387663237e17253d95fc67031117 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 22 Jun 2018 19:23:00 +0200 Subject: [PATCH] extract subkey loading from KeychainProvider --- .../keychain/livedata/KeyRingDao.java | 8 +- .../keychain/model/CustomColumnAdapters.java | 14 ++ .../keychain/model/{Key.java => SubKey.java} | 16 +- .../pgp/OpenPgpSignatureResultBuilder.java | 17 +- .../provider/CachedPublicKeyRing.java | 19 +- .../keychain/provider/KeyRepository.java | 24 +- .../keychain/provider/KeychainContract.java | 29 --- .../keychain/provider/KeychainProvider.java | 44 +--- .../keychain/ui/BackupRestoreFragment.java | 40 ++-- .../keychain/ui/CreateKeyFinalFragment.java | 27 +-- .../keychain/ui/KeyListFragment.java | 2 +- .../ui/ViewKeyAdvSubkeysFragment.java | 69 +++--- .../ui/adapter/FlexibleKeyDetailsItem.java | 2 +- .../ui/adapter/FlexibleKeyItemFactory.java | 2 +- .../keychain/ui/adapter/SubkeysAdapter.java | 207 ++++++------------ .../ui/keyview/loader/SubkeyStatusDao.java | 129 ++++------- .../org/sufficientlysecure/keychain/Keys.sq | 20 +- 17 files changed, 248 insertions(+), 421 deletions(-) rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/{Key.java => SubKey.java} (67%) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/livedata/KeyRingDao.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/livedata/KeyRingDao.java index 68efe8010..98298a094 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/livedata/KeyRingDao.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/livedata/KeyRingDao.java @@ -9,8 +9,8 @@ import android.content.Context; import android.database.Cursor; import com.squareup.sqldelight.SqlDelightQuery; -import org.sufficientlysecure.keychain.model.Key; -import org.sufficientlysecure.keychain.model.Key.UnifiedKeyInfo; +import org.sufficientlysecure.keychain.model.SubKey; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.provider.KeychainDatabase; @@ -28,11 +28,11 @@ public class KeyRingDao { } public List getUnifiedKeyInfo() { - SqlDelightQuery query = Key.FACTORY.selectAllUnifiedKeyInfo(); + SqlDelightQuery query = SubKey.FACTORY.selectAllUnifiedKeyInfo(); List result = new ArrayList<>(); try (Cursor cursor = db.query(query)) { while (cursor.moveToNext()) { - UnifiedKeyInfo unifiedKeyInfo = Key.UNIFIED_KEY_INFO_MAPPER.map(cursor); + UnifiedKeyInfo unifiedKeyInfo = SubKey.UNIFIED_KEY_INFO_MAPPER.map(cursor); result.add(unifiedKeyInfo); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java index e6b9d0ee7..4561f45fe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java @@ -7,6 +7,7 @@ import android.support.annotation.NonNull; import com.squareup.sqldelight.ColumnAdapter; import org.sufficientlysecure.keychain.model.AutocryptPeer.GossipOrigin; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; @@ -50,4 +51,17 @@ public final class CustomColumnAdapters { } } }; + + public static final ColumnAdapter SECRET_KEY_TYPE_ADAPTER = new ColumnAdapter() { + @NonNull + @Override + public SecretKeyType decode(Long databaseValue) { + return databaseValue == null ? SecretKeyType.UNAVAILABLE : SecretKeyType.fromNum(databaseValue.intValue()); + } + + @Override + public Long encode(@NonNull SecretKeyType value) { + return (long) value.getNum(); + } + }; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/Key.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/SubKey.java similarity index 67% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/Key.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/SubKey.java index 523883bc1..36102c73c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/Key.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/SubKey.java @@ -6,14 +6,23 @@ import java.util.Collections; import java.util.List; import com.google.auto.value.AutoValue; +import com.squareup.sqldelight.RowMapper; import org.sufficientlysecure.keychain.KeysModel; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; @AutoValue -public abstract class Key implements KeysModel { - public static final Factory FACTORY = new Factory<>(AutoValue_Key::new); +public abstract class SubKey implements KeysModel { + public static final Factory FACTORY = + new Factory<>(AutoValue_SubKey::new, CustomColumnAdapters.SECRET_KEY_TYPE_ADAPTER); public static final SelectAllUnifiedKeyInfoMapper UNIFIED_KEY_INFO_MAPPER = - FACTORY.selectAllUnifiedKeyInfoMapper(AutoValue_Key_UnifiedKeyInfo::new); + FACTORY.selectAllUnifiedKeyInfoMapper(AutoValue_SubKey_UnifiedKeyInfo::new); + public static Mapper SUBKEY_MAPPER = new Mapper<>(FACTORY); + public static RowMapper SKT_MAPPER = FACTORY.selectSecretKeyTypeMapper(); + + public boolean expires() { + return expiry() != null; + } @AutoValue public static abstract class UnifiedKeyInfo implements SelectAllUnifiedKeyInfoModel { @@ -45,6 +54,5 @@ public abstract class Key implements KeysModel { } return autocryptPackageNames; } - } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java index fa6f97f8e..53477da28 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp; import java.util.ArrayList; import java.util.Date; +import java.util.List; import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult.SenderStatusResult; @@ -27,7 +28,6 @@ import org.openintents.openpgp.util.OpenPgpUtils; import org.openintents.openpgp.util.OpenPgpUtils.UserId; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeyRepository; -import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import timber.log.Timber; @@ -41,8 +41,8 @@ public class OpenPgpSignatureResultBuilder { // OpenPgpSignatureResult private String mPrimaryUserId; - private ArrayList mUserIds = new ArrayList<>(); - private ArrayList mConfirmedUserIds; + private List mUserIds = new ArrayList<>(); + private List mConfirmedUserIds; private long mKeyId; private SenderStatusResult mSenderStatusResult; @@ -101,7 +101,7 @@ public class OpenPgpSignatureResultBuilder { this.mIsKeyExpired = keyExpired; } - public void setUserIds(ArrayList userIds, ArrayList confirmedUserIds) { + public void setUserIds(List userIds, List confirmedUserIds) { this.mUserIds = userIds; this.mConfirmedUserIds = confirmedUserIds; } @@ -125,9 +125,8 @@ public class OpenPgpSignatureResultBuilder { } setSignatureKeyCertified(signingRing.getVerified() > 0); - ArrayList allUserIds = signingRing.getUnorderedUserIds(); - ArrayList confirmedUserIds; - confirmedUserIds = mKeyRepository.getConfirmedUserIds(signingRing.getMasterKeyId()); + List allUserIds = signingRing.getUnorderedUserIds(); + List confirmedUserIds = mKeyRepository.getConfirmedUserIds(signingRing.getMasterKeyId()); setUserIds(allUserIds, confirmedUserIds); mSenderStatusResult = processSenderStatusResult(allUserIds, confirmedUserIds); @@ -138,7 +137,7 @@ public class OpenPgpSignatureResultBuilder { } private SenderStatusResult processSenderStatusResult( - ArrayList allUserIds, ArrayList confirmedUserIds) { + List allUserIds, List confirmedUserIds) { if (mSenderAddress == null) { return SenderStatusResult.UNKNOWN; } @@ -152,7 +151,7 @@ public class OpenPgpSignatureResultBuilder { } } - private static boolean userIdListContainsAddress(String senderAddress, ArrayList confirmedUserIds) { + private static boolean userIdListContainsAddress(String senderAddress, List confirmedUserIds) { for (String rawUserId : confirmedUserIds) { UserId userId = OpenPgpUtils.splitUserId(rawUserId); if (senderAddress.equalsIgnoreCase(userId.email)) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java index 077c72285..57957c64b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java @@ -25,7 +25,6 @@ import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import timber.log.Timber; @@ -227,10 +226,6 @@ public class CachedPublicKeyRing extends KeyRing { } } - public boolean hasSecretAuthentication() throws PgpKeyNotFoundException { - return getSecretAuthenticationId() != 0; - } - public long getAuthenticationId() throws PgpKeyNotFoundException { try { Object data = mKeyRepository.getGenericData(mUri, @@ -242,10 +237,6 @@ public class CachedPublicKeyRing extends KeyRing { } } - public boolean hasAuthentication() throws PgpKeyNotFoundException { - return getAuthenticationId() != 0; - } - @Override public int getVerified() throws PgpKeyNotFoundException { try { @@ -270,11 +261,11 @@ public class CachedPublicKeyRing extends KeyRing { } public SecretKeyType getSecretKeyType(long keyId) throws NotFoundException { - Object data = mKeyRepository.getGenericData(Keys.buildKeysUri(mUri), - KeyRings.HAS_SECRET, - KeyRepository.FIELD_TYPE_INTEGER, - KeyRings.KEY_ID + " = " + Long.toString(keyId)); - return SecretKeyType.fromNum(((Long) data).intValue()); + SecretKeyType secretKeyType = mKeyRepository.getSecretKeyType(keyId); + if (secretKeyType == null) { + throw new NotFoundException(); + } + return secretKeyType; } public byte[] getEncoded() throws PgpKeyNotFoundException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java index 43a92f4e0..ce89cc977 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java @@ -32,11 +32,13 @@ import android.net.Uri; import com.squareup.sqldelight.SqlDelightQuery; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.sufficientlysecure.keychain.model.KeyRingPublic; +import org.sufficientlysecure.keychain.model.SubKey; import org.sufficientlysecure.keychain.model.UserPacket; import org.sufficientlysecure.keychain.model.UserPacket.UserId; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; @@ -119,11 +121,6 @@ public class KeyRepository extends AbstractDao { return result; } - Object getGenericData(Uri uri, String column, int type, String selection) - throws NotFoundException { - return getGenericData(uri, new String[]{column}, new int[]{type}, selection).get(column); - } - private HashMap getGenericData(Uri uri, String[] proj, int[] types) throws NotFoundException { return getGenericData(uri, proj, types, null); @@ -246,7 +243,7 @@ public class KeyRepository extends AbstractDao { return mapAllRows(query, UserPacket.USER_ID_MAPPER::map); } - public ArrayList getConfirmedUserIds(long masterKeyId) { + public List getConfirmedUserIds(long masterKeyId) { ArrayList userIds = new ArrayList<>(); SqlDelightQuery query = UserPacket.FACTORY.selectUserIdsByMasterKeyIdAndVerification(masterKeyId, Certs.VERIFIED_SECRET); @@ -256,6 +253,21 @@ public class KeyRepository extends AbstractDao { return userIds; } + public List getSubKeysByMasterKeyId(long masterKeyId) { + SqlDelightQuery query = SubKey.FACTORY.selectSubkeysByMasterKeyId(masterKeyId); + return mapAllRows(query, SubKey.SUBKEY_MAPPER::map); + } + + public SecretKeyType getSecretKeyType(long keyId) { + SqlDelightQuery query = SubKey.FACTORY.selectSecretKeyType(keyId); + try (Cursor cursor = getReadableDb().query(query)) { + if (cursor.moveToFirst()) { + return SubKey.SKT_MAPPER.map(cursor); + } + return null; + } + } + private byte[] getKeyRingAsArmoredData(byte[] data) throws IOException, PgpGeneralException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ArmoredOutputStream aos = new ArmoredOutputStream(bos); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 21edee446..0a39dc397 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -130,11 +130,6 @@ public class KeychainContract { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); - public static final String CONTENT_TYPE - = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.key_rings"; - public static final String CONTENT_ITEM_TYPE - = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.key_rings"; - public static Uri buildUnifiedKeyRingsUri() { return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build(); } @@ -194,34 +189,15 @@ public class KeychainContract { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); - /** - * Use if multiple items get returned - */ - public static final String CONTENT_TYPE - = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.keychain.keys"; - - /** - * Use if a single item is returned - */ - public static final String CONTENT_ITEM_TYPE - = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.keychain.keys"; - public static Uri buildKeysUri(long masterKeyId) { return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_KEYS).build(); } - public static Uri buildKeysUri(Uri uri) { - return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_KEYS).build(); - } - } public static class KeySignatures implements KeySignaturesColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_SIGNATURES).build(); - - public static final String CONTENT_TYPE - = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.key_signatures"; } public static class UserPackets implements UserPacketsColumns, BaseColumns { @@ -235,11 +211,6 @@ public class KeychainContract { } public static class Certs implements CertsColumns, BaseColumns { - public static final String USER_ID = UserPacketsColumns.USER_ID; - public static final String NAME = UserPacketsColumns.NAME; - public static final String EMAIL = UserPacketsColumns.EMAIL; - public static final String COMMENT = UserPacketsColumns.COMMENT; - public static final int UNVERIFIED = 0; public static final int VERIFIED_SECRET = 1; public static final int VERIFIED_SELF = 2; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 48efb2781..40fae66eb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -168,17 +168,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe */ @Override public String getType(@NonNull Uri uri) { - final int match = mUriMatcher.match(uri); - switch (match) { - case KEY_RING_KEYS: - return Keys.CONTENT_TYPE; - - case KEY_SIGNATURES: - return KeySignatures.CONTENT_TYPE; - - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } + throw new UnsupportedOperationException(); } /** @@ -194,7 +184,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe int match = mUriMatcher.match(uri); // all query() parameters, for good measure - String groupBy = null, having = null; + String groupBy; switch (match) { case KEY_RING_UNIFIED: @@ -431,34 +421,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe break; } - case KEY_RING_KEYS: { - HashMap projectionMap = new HashMap<>(); - projectionMap.put(Keys._ID, Tables.KEYS + ".oid AS _id"); - projectionMap.put(Keys.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID); - projectionMap.put(Keys.RANK, Tables.KEYS + "." + Keys.RANK); - projectionMap.put(Keys.KEY_ID, Keys.KEY_ID); - projectionMap.put(Keys.KEY_SIZE, Keys.KEY_SIZE); - projectionMap.put(Keys.KEY_CURVE_OID, Keys.KEY_CURVE_OID); - projectionMap.put(Keys.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED); - projectionMap.put(Keys.IS_SECURE, Tables.KEYS + "." + Keys.IS_SECURE); - projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY); - projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT); - projectionMap.put(Keys.CAN_SIGN, Keys.CAN_SIGN); - projectionMap.put(Keys.CAN_AUTHENTICATE, Keys.CAN_AUTHENTICATE); - projectionMap.put(Keys.HAS_SECRET, Keys.HAS_SECRET); - projectionMap.put(Keys.CREATION, Keys.CREATION); - projectionMap.put(Keys.EXPIRY, Keys.EXPIRY); - projectionMap.put(Keys.ALGORITHM, Keys.ALGORITHM); - projectionMap.put(Keys.FINGERPRINT, Keys.FINGERPRINT); - qb.setProjectionMap(projectionMap); - - qb.setTables(Tables.KEYS); - qb.appendWhere(Keys.MASTER_KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(1)); - - break; - } - default: { throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")"); } @@ -489,7 +451,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); + String rawQuery = qb.buildQuery(projection, selection, groupBy, null, orderBy, null); DatabaseUtil.explainQuery(db, rawQuery); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupRestoreFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupRestoreFragment.java index 682b0715a..84c2882d7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupRestoreFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupRestoreFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; + import java.util.ArrayList; import java.util.Iterator; @@ -36,10 +37,10 @@ import android.view.View; import android.view.ViewGroup; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.model.SubKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; -import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.FileHelper; @@ -138,32 +139,19 @@ public class BackupRestoreFragment extends Fragment { } private Long getFirstSubKeyWithPassphrase(long masterKeyId, ContentResolver resolver) { - Cursor cursor = resolver.query( - KeychainContract.Keys.buildKeysUri(masterKeyId), new String[]{ - Keys.KEY_ID, - Keys.HAS_SECRET, - }, Keys.HAS_SECRET + " != 0", null, null); - try { - if (cursor != null) { - while(cursor.moveToNext()) { - SecretKeyType secretKeyType = SecretKeyType.fromNum(cursor.getInt(1)); - switch (secretKeyType) { - case PASSPHRASE_EMPTY: - case DIVERT_TO_CARD: - case UNAVAILABLE: - return null; - case GNU_DUMMY: - continue; - default: { - return cursor.getLong(0); - } - } + KeyRepository keyRepository = KeyRepository.create(requireContext()); + for (SubKey subKey : keyRepository.getSubKeysByMasterKeyId(masterKeyId)) { + switch (subKey.has_secret()) { + case PASSPHRASE_EMPTY: + case DIVERT_TO_CARD: + case UNAVAILABLE: + return null; + case GNU_DUMMY: + continue; + default: { + return subKey.key_id(); } } - } finally { - if (cursor != null) { - cursor.close(); - } } return null; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index f8431bf3f..f1446a26d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -20,11 +20,11 @@ package org.sufficientlysecure.keychain.ui; import java.util.Date; import java.util.Iterator; +import java.util.List; import java.util.regex.Pattern; import android.app.Activity; import android.content.Intent; -import android.database.Cursor; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; @@ -43,6 +43,7 @@ import org.openintents.openpgp.util.OpenPgpUtils; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress; +import org.sufficientlysecure.keychain.model.SubKey; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.UploadResult; @@ -50,7 +51,6 @@ import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; -import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.service.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; @@ -410,31 +410,20 @@ public class CreateKeyFinalFragment extends Fragment { private void moveToCard(final EditKeyResult saveKeyResult) { CreateKeyActivity activity = (CreateKeyActivity) getActivity(); + KeyRepository keyRepository = KeyRepository.create(getContext()); SaveKeyringParcel.Builder builder; - CachedPublicKeyRing key = (KeyRepository.create(getContext())) - .getCachedPublicKeyRing(saveKeyResult.mMasterKeyId); + CachedPublicKeyRing key = keyRepository.getCachedPublicKeyRing(saveKeyResult.mMasterKeyId); try { - builder = SaveKeyringParcel.buildChangeKeyringParcel(key.getMasterKeyId(), key.getFingerprint()); + builder = SaveKeyringParcel.buildChangeKeyringParcel(saveKeyResult.mMasterKeyId, key.getFingerprint()); } catch (PgpKeyNotFoundException e) { Timber.e("Key that should be moved to Security Token not found in database!"); return; } - // define subkeys that should be moved to the card - Cursor cursor = activity.getContentResolver().query( - KeychainContract.Keys.buildKeysUri(builder.getMasterKeyId()), - new String[]{KeychainContract.Keys.KEY_ID,}, null, null, null - ); - try { - while (cursor != null && cursor.moveToNext()) { - long subkeyId = cursor.getLong(0); - builder.addOrReplaceSubkeyChange(SubkeyChange.createMoveToSecurityTokenChange(subkeyId)); - } - } finally { - if (cursor != null) { - cursor.close(); - } + List subKeys = keyRepository.getSubKeysByMasterKeyId(saveKeyResult.mMasterKeyId); + for (SubKey subKey : subKeys) { + builder.addOrReplaceSubkeyChange(SubkeyChange.createMoveToSecurityTokenChange(subKey.key_id())); } // define new PIN and Admin PIN for the card diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 7f28946b1..4a202043c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -59,7 +59,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.keysync.KeyserverSyncManager; import org.sufficientlysecure.keychain.livedata.KeyRingDao; -import org.sufficientlysecure.keychain.model.Key.UnifiedKeyInfo; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.operations.results.BenchmarkResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.PgpHelper; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java index bff3d5695..1d5eff9fe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java @@ -18,6 +18,9 @@ package org.sufficientlysecure.keychain.ui; +import java.util.List; + +import android.arch.lifecycle.LiveData; import android.content.Intent; import android.database.Cursor; import android.net.Uri; @@ -41,8 +44,11 @@ import android.widget.ViewAnimator; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; +import org.sufficientlysecure.keychain.livedata.GenericLiveData; +import org.sufficientlysecure.keychain.model.SubKey; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; +import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; @@ -62,7 +68,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements public static final String ARG_DATA_URI = "data_uri"; private static final int LOADER_ID_UNIFIED = 0; - private static final int LOADER_ID_SUBKEYS = 1; private ListView mSubkeysList; private ListView mSubkeysAddedList; @@ -78,7 +83,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements private long mMasterKeyId; private byte[] mFingerprint; - private boolean mHasSecret; private SaveKeyringParcel.Builder mEditModeSkpBuilder; @Override @@ -148,13 +152,14 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements mDataUri = dataUri; // Create an empty adapter we will use to display the loaded data. - mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0); + mSubkeysAdapter = new SubkeysAdapter(requireContext()); mSubkeysList.setAdapter(mSubkeysAdapter); // Prepare the loaders. Either re-connect with an existing ones, // or start new ones. getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); - getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, this); + + setContentShown(false); } // These are the rows that we will retrieve. @@ -178,14 +183,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements PROJECTION, null, null, null); } - case LOADER_ID_SUBKEYS: { - setContentShown(false); - - Uri subkeysUri = KeychainContract.Keys.buildKeysUri(mDataUri); - return new CursorLoader(getActivity(), subkeysUri, - SubkeysAdapter.SUBKEYS_PROJECTION, null, null, null); - } - default: return null; } @@ -202,29 +199,29 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements data.moveToFirst(); mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); - mHasSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; mFingerprint = data.getBlob(INDEX_FINGERPRINT); - break; - } - case LOADER_ID_SUBKEYS: { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mSubkeysAdapter.swapCursor(data); - // TODO: maybe show not before both are loaded! - setContentShown(true); + KeyRepository keyRepository = KeyRepository.create(requireContext()); + LiveData> subKeyLiveData = new GenericLiveData<>(requireContext(), null, + () -> keyRepository.getSubKeysByMasterKeyId(mMasterKeyId)); + subKeyLiveData.observe(this, this::onLoadSubKeys); break; } } } + private void onLoadSubKeys(List subKeys) { + mSubkeysAdapter.setData(subKeys); + setContentShown(true); + } + /** * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. * We need to make sure we are no longer using it. */ public void onLoaderReset(Loader loader) { - mSubkeysAdapter.swapCursor(null); + } @Override @@ -256,7 +253,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements mSubkeyAddFabLayout.setDisplayedChild(1); mSubkeysAdapter.setEditMode(mEditModeSkpBuilder); - getLoaderManager().restartLoader(LOADER_ID_SUBKEYS, null, ViewKeyAdvSubkeysFragment.this); mode.setTitle(R.string.title_edit_subkeys); mode.getMenuInflater().inflate(R.menu.action_edit_uids, menu); @@ -281,7 +277,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements mSubkeysAdapter.setEditMode(null); mSubkeysAddedLayout.setVisibility(View.GONE); mSubkeyAddFabLayout.setDisplayedChild(0); - getLoaderManager().restartLoader(LOADER_ID_SUBKEYS, null, ViewKeyAdvSubkeysFragment.this); } }); } @@ -309,7 +304,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements } private void editSubkey(final int position) { - final long keyId = mSubkeysAdapter.getKeyId(position); + final SubKey subKey = mSubkeysAdapter.getItem(position); Handler returnHandler = new Handler() { @Override @@ -320,29 +315,28 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements break; case EditSubkeyDialogFragment.MESSAGE_REVOKE: // toggle - if (mEditModeSkpBuilder.getMutableRevokeSubKeys().contains(keyId)) { - mEditModeSkpBuilder.removeRevokeSubkey(keyId); + if (mEditModeSkpBuilder.getMutableRevokeSubKeys().contains(subKey.key_id())) { + mEditModeSkpBuilder.removeRevokeSubkey(subKey.key_id()); } else { - mEditModeSkpBuilder.addRevokeSubkey(keyId); + mEditModeSkpBuilder.addRevokeSubkey(subKey.key_id()); } break; case EditSubkeyDialogFragment.MESSAGE_STRIP: { - SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position); - if (secretKeyType == SecretKeyType.GNU_DUMMY) { + if (subKey.has_secret() == SecretKeyType.GNU_DUMMY) { // Key is already stripped; this is a no-op. break; } - SubkeyChange change = mEditModeSkpBuilder.getSubkeyChange(keyId); + SubkeyChange change = mEditModeSkpBuilder.getSubkeyChange(subKey.key_id()); if (change == null || !change.getDummyStrip()) { - mEditModeSkpBuilder.addOrReplaceSubkeyChange(SubkeyChange.createStripChange(keyId)); + mEditModeSkpBuilder.addOrReplaceSubkeyChange(SubkeyChange.createStripChange(subKey.key_id())); } else { mEditModeSkpBuilder.removeSubkeyChange(change); } break; } } - getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad(); + mSubkeysAdapter.notifyDataSetChanged(); } }; @@ -360,9 +354,10 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements } private void editSubkeyExpiry(final int position) { - final long keyId = mSubkeysAdapter.getKeyId(position); - final Long creationDate = mSubkeysAdapter.getCreationDate(position); - final Long expiryDate = mSubkeysAdapter.getExpiryDate(position); + SubKey subKey = mSubkeysAdapter.getItem(position); + final long keyId = subKey.key_id(); + final Long creationDate = subKey.creation(); + final Long expiryDate = subKey.expiry(); Handler returnHandler = new Handler() { @Override @@ -375,7 +370,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements SubkeyChange.createFlagsOrExpiryChange(keyId, null, expiry)); break; } - getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad(); + mSubkeysAdapter.notifyDataSetChanged(); } }; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/FlexibleKeyDetailsItem.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/FlexibleKeyDetailsItem.java index 49a188d96..cf53b113f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/FlexibleKeyDetailsItem.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/FlexibleKeyDetailsItem.java @@ -17,7 +17,7 @@ import eu.davidea.flexibleadapter.items.IFilterable; import eu.davidea.flexibleadapter.items.IFlexible; import eu.davidea.viewholders.FlexibleViewHolder; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.model.Key.UnifiedKeyInfo; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyDetailsItem.FlexibleKeyItemViewHolder; import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyItem.FlexibleSectionableKeyItem; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/FlexibleKeyItemFactory.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/FlexibleKeyItemFactory.java index abf5007a8..3008dd4ac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/FlexibleKeyItemFactory.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/FlexibleKeyItemFactory.java @@ -10,7 +10,7 @@ import android.content.res.Resources; import android.support.annotation.NonNull; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.model.Key.UnifiedKeyInfo; +import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; public class FlexibleKeyItemFactory { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java index 2d326cba5..3be6e03a0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java @@ -20,15 +20,14 @@ package org.sufficientlysecure.keychain.ui.adapter; import java.util.Calendar; import java.util.Date; +import java.util.List; import java.util.TimeZone; import android.content.Context; import android.content.res.ColorStateList; -import android.database.Cursor; import android.graphics.PorterDuff; import android.graphics.Typeface; import android.support.annotation.Nullable; -import android.support.v4.widget.CursorAdapter; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -37,121 +36,64 @@ import android.text.style.StyleSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; -import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.model.SubKey; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -public class SubkeysAdapter extends CursorAdapter { - private LayoutInflater mInflater; +public class SubkeysAdapter extends BaseAdapter { + private final Context context; + private final LayoutInflater layoutInflater; + + private List data; private SaveKeyringParcel.Builder mSkpBuilder; - private boolean mHasAnySecret; private ColorStateList mDefaultTextColor; - public static final String[] SUBKEYS_PROJECTION = new String[]{ - Keys._ID, - Keys.KEY_ID, - Keys.RANK, - Keys.ALGORITHM, - Keys.KEY_SIZE, - Keys.KEY_CURVE_OID, - Keys.HAS_SECRET, - Keys.CAN_CERTIFY, - Keys.CAN_ENCRYPT, - Keys.CAN_SIGN, - Keys.CAN_AUTHENTICATE, - Keys.IS_REVOKED, - Keys.IS_SECURE, - Keys.CREATION, - Keys.EXPIRY, - Keys.FINGERPRINT - }; - private static final int INDEX_ID = 0; - private static final int INDEX_KEY_ID = 1; - private static final int INDEX_RANK = 2; - private static final int INDEX_ALGORITHM = 3; - private static final int INDEX_KEY_SIZE = 4; - private static final int INDEX_KEY_CURVE_OID = 5; - private static final int INDEX_HAS_SECRET = 6; - private static final int INDEX_CAN_CERTIFY = 7; - private static final int INDEX_CAN_ENCRYPT = 8; - private static final int INDEX_CAN_SIGN = 9; - private static final int INDEX_CAN_AUTHENTICATE = 10; - private static final int INDEX_IS_REVOKED = 11; - private static final int INDEX_IS_SECURE = 12; - private static final int INDEX_CREATION = 13; - private static final int INDEX_EXPIRY = 14; - private static final int INDEX_FINGERPRINT = 15; - - public SubkeysAdapter(Context context, Cursor c, int flags) { - super(context, c, flags); - - mInflater = LayoutInflater.from(context); + public SubkeysAdapter(Context context) { + this.context = context; + layoutInflater = LayoutInflater.from(context); } - public long getKeyId(int position) { - mCursor.moveToPosition(position); - return mCursor.getLong(INDEX_KEY_ID); + public void setData(List data) { + this.data = data; + notifyDataSetChanged(); } - public long getCreationDate(int position) { - mCursor.moveToPosition(position); - return mCursor.getLong(INDEX_CREATION); + @Override + public int getCount() { + return data != null ? data.size() : 0; } - public Long getExpiryDate(int position) { - mCursor.moveToPosition(position); - if (mCursor.isNull(INDEX_EXPIRY)) { - return null; + @Override + public SubKey getItem(int position) { + return data.get(position); + } + + @Override + public long getItemId(int position) { + return data.get(position).key_id(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView != null) { + view = convertView; } else { - return mCursor.getLong(INDEX_EXPIRY); - } - } - - public int getAlgorithm(int position) { - mCursor.moveToPosition(position); - return mCursor.getInt(INDEX_ALGORITHM); - } - - public int getKeySize(int position) { - mCursor.moveToPosition(position); - return mCursor.getInt(INDEX_KEY_SIZE); - } - - public String getCurveOid(int position) { - mCursor.moveToPosition(position); - return mCursor.getString(INDEX_KEY_CURVE_OID); - } - - public SecretKeyType getSecretKeyType(int position) { - mCursor.moveToPosition(position); - return SecretKeyType.fromNum(mCursor.getInt(INDEX_HAS_SECRET)); - } - - @Override - public Cursor swapCursor(Cursor newCursor) { - mHasAnySecret = false; - if (newCursor != null && newCursor.moveToFirst()) { - do { - SecretKeyType hasSecret = SecretKeyType.fromNum(newCursor.getInt(INDEX_HAS_SECRET)); - if (hasSecret.isUsable()) { - mHasAnySecret = true; - break; - } - } while (newCursor.moveToNext()); + view = layoutInflater.inflate(R.layout.view_key_adv_subkey_item, parent, false); } - return super.swapCursor(newCursor); - } + if (mDefaultTextColor == null) { + TextView keyId = view.findViewById(R.id.subkey_item_key_id); + mDefaultTextColor = keyId.getTextColors(); + } - @Override - public void bindView(View view, Context context, Cursor cursor) { TextView vKeyId = view.findViewById(R.id.subkey_item_key_id); TextView vKeyDetails = view.findViewById(R.id.subkey_item_details); TextView vKeyExpiry = view.findViewById(R.id.subkey_item_expiry); @@ -166,19 +108,20 @@ public class SubkeysAdapter extends CursorAdapter { ImageView deleteImage = view.findViewById(R.id.subkey_item_delete_button); deleteImage.setVisibility(View.GONE); - long keyId = cursor.getLong(INDEX_KEY_ID); - vKeyId.setText(KeyFormattingUtils.beautifyKeyId(keyId)); + SubKey subKey = getItem(position); + + vKeyId.setText(KeyFormattingUtils.beautifyKeyId(subKey.key_id())); // may be set with additional "stripped" later on SpannableStringBuilder algorithmStr = new SpannableStringBuilder(); algorithmStr.append(KeyFormattingUtils.getAlgorithmInfo( context, - cursor.getInt(INDEX_ALGORITHM), - cursor.getInt(INDEX_KEY_SIZE), - cursor.getString(INDEX_KEY_CURVE_OID) + subKey.algorithm(), + subKey.key_size(), + subKey.key_curve_oid() )); - SubkeyChange change = mSkpBuilder != null ? mSkpBuilder.getSubkeyChange(keyId) : null; + SubkeyChange change = mSkpBuilder != null ? mSkpBuilder.getSubkeyChange(subKey.key_id()) : null; if (change != null && (change.getDummyStrip() || change.getMoveKeyToSecurityToken())) { if (change.getDummyStrip()) { algorithmStr.append(", "); @@ -197,7 +140,7 @@ public class SubkeysAdapter extends CursorAdapter { algorithmStr.append(boldDivert); } } else { - switch (SecretKeyType.fromNum(cursor.getInt(INDEX_HAS_SECRET))) { + switch (subKey.has_secret()) { case GNU_DUMMY: algorithmStr.append(", "); algorithmStr.append(context.getString(R.string.key_stripped)); @@ -218,7 +161,7 @@ public class SubkeysAdapter extends CursorAdapter { } vKeyDetails.setText(algorithmStr, TextView.BufferType.SPANNABLE); - boolean isMasterKey = cursor.getInt(INDEX_RANK) == 0; + boolean isMasterKey = subKey.rank() == 0; if (isMasterKey) { vKeyId.setTypeface(null, Typeface.BOLD); } else { @@ -226,22 +169,21 @@ public class SubkeysAdapter extends CursorAdapter { } // Set icons according to properties - vCertifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE); - vEncryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE); - vSignIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE); - vAuthenticateIcon.setVisibility(cursor.getInt(INDEX_CAN_AUTHENTICATE) != 0 ? View.VISIBLE : View.GONE); + vCertifyIcon.setVisibility(subKey.can_certify() ? View.VISIBLE : View.GONE); + vEncryptIcon.setVisibility(subKey.can_encrypt() ? View.VISIBLE : View.GONE); + vSignIcon.setVisibility(subKey.can_sign() ? View.VISIBLE : View.GONE); + vAuthenticateIcon.setVisibility(subKey.can_authenticate() ? View.VISIBLE : View.GONE); - boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - boolean isSecure = cursor.getInt(INDEX_IS_SECURE) > 0; + boolean isRevoked = subKey.is_revoked(); Date expiryDate = null; - if (!cursor.isNull(INDEX_EXPIRY)) { - expiryDate = new Date(cursor.getLong(INDEX_EXPIRY) * 1000); + if (subKey.expires()) { + expiryDate = new Date(subKey.expiry() * 1000); } // for edit key if (mSkpBuilder != null) { - boolean revokeThisSubkey = (mSkpBuilder.getMutableRevokeSubKeys().contains(keyId)); + boolean revokeThisSubkey = (mSkpBuilder.getMutableRevokeSubKeys().contains(subKey.key_id())); if (revokeThisSubkey) { if (!isRevoked) { @@ -249,7 +191,7 @@ public class SubkeysAdapter extends CursorAdapter { } } - SaveKeyringParcel.SubkeyChange subkeyChange = mSkpBuilder.getSubkeyChange(keyId); + SaveKeyringParcel.SubkeyChange subkeyChange = mSkpBuilder.getSubkeyChange(subKey.key_id()); if (subkeyChange != null) { if (subkeyChange.getExpiry() == null || subkeyChange.getExpiry() == 0L) { expiryDate = null; @@ -280,37 +222,37 @@ public class SubkeysAdapter extends CursorAdapter { } // if key is expired or revoked... - boolean isInvalid = isRevoked || isExpired || !isSecure; + boolean isInvalid = isRevoked || isExpired || !subKey.is_secure(); if (isInvalid) { vStatus.setVisibility(View.VISIBLE); vCertifyIcon.setColorFilter( - mContext.getResources().getColor(R.color.key_flag_gray), + context.getResources().getColor(R.color.key_flag_gray), PorterDuff.Mode.SRC_IN); vSignIcon.setColorFilter( - mContext.getResources().getColor(R.color.key_flag_gray), + context.getResources().getColor(R.color.key_flag_gray), PorterDuff.Mode.SRC_IN); vEncryptIcon.setColorFilter( - mContext.getResources().getColor(R.color.key_flag_gray), + context.getResources().getColor(R.color.key_flag_gray), PorterDuff.Mode.SRC_IN); vAuthenticateIcon.setColorFilter( - mContext.getResources().getColor(R.color.key_flag_gray), + context.getResources().getColor(R.color.key_flag_gray), PorterDuff.Mode.SRC_IN); if (isRevoked) { vStatus.setImageResource(R.drawable.status_signature_revoked_cutout_24dp); vStatus.setColorFilter( - mContext.getResources().getColor(R.color.key_flag_gray), + context.getResources().getColor(R.color.key_flag_gray), PorterDuff.Mode.SRC_IN); } else if (isExpired) { vStatus.setImageResource(R.drawable.status_signature_expired_cutout_24dp); vStatus.setColorFilter( - mContext.getResources().getColor(R.color.key_flag_gray), + context.getResources().getColor(R.color.key_flag_gray), PorterDuff.Mode.SRC_IN); - } else if (!isSecure) { + } else if (!subKey.is_secure()) { vStatus.setImageResource(R.drawable.status_signature_invalid_cutout_24dp); vStatus.setColorFilter( - mContext.getResources().getColor(R.color.key_flag_gray), + context.getResources().getColor(R.color.key_flag_gray), PorterDuff.Mode.SRC_IN); } } else { @@ -328,36 +270,20 @@ public class SubkeysAdapter extends CursorAdapter { vKeyId.setEnabled(!isInvalid); vKeyDetails.setEnabled(!isInvalid); vKeyExpiry.setEnabled(!isInvalid); - } - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - View view = mInflater.inflate(R.layout.view_key_adv_subkey_item, null); - if (mDefaultTextColor == null) { - TextView keyId = view.findViewById(R.id.subkey_item_key_id); - mDefaultTextColor = keyId.getTextColors(); - } return view; } // Disable selection of items, http://stackoverflow.com/a/4075045 @Override public boolean areAllItemsEnabled() { - if (mSkpBuilder == null) { - return false; - } else { - return super.areAllItemsEnabled(); - } + return mSkpBuilder != null && super.areAllItemsEnabled(); } // Disable selection of items, http://stackoverflow.com/a/4075045 @Override public boolean isEnabled(int position) { - if (mSkpBuilder == null) { - return false; - } else { - return super.isEnabled(position); - } + return mSkpBuilder != null && super.isEnabled(position); } /** Set this adapter into edit mode. This mode displays additional info for @@ -372,6 +298,7 @@ public class SubkeysAdapter extends CursorAdapter { */ public void setEditMode(@Nullable SaveKeyringParcel.Builder builder) { mSkpBuilder = builder; + notifyDataSetChanged(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/SubkeyStatusDao.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/SubkeyStatusDao.java index 8caf8b261..5ea41d801 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/SubkeyStatusDao.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/SubkeyStatusDao.java @@ -24,97 +24,59 @@ import java.util.Comparator; import java.util.Date; import java.util.List; -import android.content.ContentResolver; import android.content.Context; -import android.database.Cursor; import android.support.annotation.NonNull; +import org.sufficientlysecure.keychain.model.SubKey; 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 timber.log.Timber; +import org.sufficientlysecure.keychain.provider.KeyRepository; public class SubkeyStatusDao { - public static final String[] PROJECTION = new String[] { - Keys.KEY_ID, - Keys.CREATION, - Keys.CAN_CERTIFY, - Keys.CAN_SIGN, - Keys.CAN_ENCRYPT, - Keys.HAS_SECRET, - Keys.EXPIRY, - Keys.IS_REVOKED, - Keys.ALGORITHM, - Keys.KEY_SIZE, - Keys.KEY_CURVE_OID - }; - private static final int INDEX_KEY_ID = 0; - private static final int INDEX_CREATION = 1; - private static final int INDEX_CAN_CERTIFY = 2; - private static final int INDEX_CAN_SIGN = 3; - private static final int INDEX_CAN_ENCRYPT = 4; - private static final int INDEX_HAS_SECRET = 5; - private static final int INDEX_EXPIRY = 6; - private static final int INDEX_IS_REVOKED = 7; - private static final int INDEX_ALGORITHM = 8; - private static final int INDEX_KEY_SIZE = 9; - private static final int INDEX_KEY_CURVE_OID = 10; - - - private final ContentResolver contentResolver; + private final KeyRepository keyRepository; public static SubkeyStatusDao getInstance(Context context) { - ContentResolver contentResolver = context.getContentResolver(); - return new SubkeyStatusDao(contentResolver); + KeyRepository keyRepository = KeyRepository.create(context); + return new SubkeyStatusDao(keyRepository); } - private SubkeyStatusDao(ContentResolver contentResolver) { - this.contentResolver = contentResolver; + private SubkeyStatusDao(KeyRepository keyRepository) { + this.keyRepository = keyRepository; } KeySubkeyStatus getSubkeyStatus(long masterKeyId, Comparator comparator) { - Cursor cursor = contentResolver.query(Keys.buildKeysUri(masterKeyId), PROJECTION, null, null, null); - if (cursor == null) { - Timber.e("Error loading key items!"); + SubKeyItem keyCertify = null; + ArrayList keysSign = new ArrayList<>(); + ArrayList keysEncrypt = new ArrayList<>(); + for (SubKey subKey : keyRepository.getSubKeysByMasterKeyId(masterKeyId)) { + SubKeyItem ski = new SubKeyItem(masterKeyId, subKey); + + if (ski.mKeyId == masterKeyId) { + keyCertify = ski; + } + + if (ski.mCanSign) { + keysSign.add(ski); + } + if (ski.mCanEncrypt) { + keysEncrypt.add(ski); + } + } + + if (keyCertify == null) { + if (!keysSign.isEmpty() || !keysEncrypt.isEmpty()) { + throw new IllegalStateException("Certification key can't be missing for a key that hasn't been deleted!"); + } return null; } - try { - SubKeyItem keyCertify = null; - ArrayList keysSign = new ArrayList<>(); - ArrayList keysEncrypt = new ArrayList<>(); - while (cursor.moveToNext()) { - SubKeyItem ski = new SubKeyItem(masterKeyId, cursor); + Collections.sort(keysSign, comparator); + Collections.sort(keysEncrypt, comparator); - if (ski.mKeyId == masterKeyId) { - keyCertify = ski; - } - - if (ski.mCanSign) { - keysSign.add(ski); - } - if (ski.mCanEncrypt) { - keysEncrypt.add(ski); - } - } - - if (keyCertify == null) { - 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); - Collections.sort(keysEncrypt, comparator); - - return new KeySubkeyStatus(keyCertify, keysSign, keysEncrypt); - } finally { - cursor.close(); - } + return new KeySubkeyStatus(keyCertify, keysSign, keysEncrypt); } public static class KeySubkeyStatus { @@ -131,7 +93,6 @@ public class SubkeyStatusDao { } public static class SubKeyItem { - final int mPosition; final long mKeyId; final Date mCreation; public final SecretKeyType mSecretKeyType; @@ -140,25 +101,23 @@ public class SubkeyStatusDao { final boolean mCanCertify, mCanSign, mCanEncrypt; public final KeySecurityProblem mSecurityProblem; - SubKeyItem(long masterKeyId, Cursor cursor) { - mPosition = cursor.getPosition(); + SubKeyItem(long masterKeyId, SubKey subKey) { + mKeyId = subKey.key_id(); + mCreation = new Date(subKey.creation() * 1000); - mKeyId = cursor.getLong(INDEX_KEY_ID); - mCreation = new Date(cursor.getLong(INDEX_CREATION) * 1000); + mSecretKeyType = subKey.has_secret(); - mSecretKeyType = SecretKeyType.fromNum(cursor.getInt(INDEX_HAS_SECRET)); - - mIsRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - mExpiry = cursor.isNull(INDEX_EXPIRY) ? null : new Date(cursor.getLong(INDEX_EXPIRY) * 1000); + mIsRevoked = subKey.is_revoked(); + mExpiry = subKey.expiry() == null ? null : new Date(subKey.expiry() * 1000); mIsExpired = mExpiry != null && mExpiry.before(new Date()); - mCanCertify = cursor.getInt(INDEX_CAN_CERTIFY) > 0; - mCanSign = cursor.getInt(INDEX_CAN_SIGN) > 0; - mCanEncrypt = cursor.getInt(INDEX_CAN_ENCRYPT) > 0; + mCanCertify = subKey.can_certify(); + mCanSign = subKey.can_sign(); + mCanEncrypt = subKey.can_encrypt(); - int algorithm = cursor.getInt(INDEX_ALGORITHM); - Integer bitStrength = cursor.isNull(INDEX_KEY_SIZE) ? null : cursor.getInt(INDEX_KEY_SIZE); - String curveOid = cursor.getString(INDEX_KEY_CURVE_OID); + int algorithm = subKey.algorithm(); + Integer bitStrength = subKey.key_size(); + String curveOid = subKey.key_curve_oid(); mSecurityProblem = PgpSecurityConstants.getKeySecurityProblem( masterKeyId, mKeyId, algorithm, bitStrength, curveOid); diff --git a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq index 4937ace06..f69194653 100644 --- a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq +++ b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq @@ -1,19 +1,20 @@ import java.lang.Boolean; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; CREATE TABLE IF NOT EXISTS keys ( master_key_id INTEGER NOT NULL, rank INTEGER NOT NULL, key_id INTEGER NOT NULL, - key_size INTEGER, + key_size INTEGER AS Integer, key_curve_oid TEXT, - algorithm INTEGER NOT NULL, + algorithm INTEGER AS Integer NOT NULL, fingerprint BLOB NOT NULL, can_certify INTEGER AS Boolean NOT NULL, can_sign INTEGER AS Boolean NOT NULL, can_encrypt INTEGER AS Boolean NOT NULL, can_authenticate INTEGER AS Boolean NOT NULL, is_revoked INTEGER AS Boolean NOT NULL, - has_secret INTEGER AS Boolean NOT NULL, + has_secret INTEGER AS SecretKeyType NOT NULL, is_secure INTEGER AS Boolean NOT NULL, creation INTEGER NOT NULL, expiry INTEGER, @@ -34,4 +35,15 @@ SELECT keys.master_key_id, MIN(user_packets.rank), user_packets.name, user_packe LEFT JOIN autocrypt_peers AS aTI ON ( aTI.master_key_id = keys.master_key_id ) WHERE keys.rank = 0 GROUP BY keys.master_key_id - ORDER BY has_secret DESC, user_packets.name COLLATE NOCASE ASC; \ No newline at end of file + ORDER BY has_secret DESC, user_packets.name COLLATE NOCASE ASC; + +selectSubkeysByMasterKeyId: +SELECT * + FROM keys + WHERE master_key_id = ? + ORDER BY rank ASC; + +selectSecretKeyType: +SELECT has_secret + FROM keys + WHERE key_id = ?; \ No newline at end of file