extract subkey loading from KeychainProvider
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<SubKey> 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<List<SubKey>> subKeyLiveData = new GenericLiveData<>(requireContext(), null,
|
||||
() -> keyRepository.getSubKeysByMasterKeyId(mMasterKeyId));
|
||||
subKeyLiveData.observe(this, this::onLoadSubKeys);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void onLoadSubKeys(List<SubKey> 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<Cursor> 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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<SubKey> 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<SubKey> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<SubKeyItem> 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<SubKeyItem> keysSign = new ArrayList<>();
|
||||
ArrayList<SubKeyItem> 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<SubKeyItem> keysSign = new ArrayList<>();
|
||||
ArrayList<SubKeyItem> 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);
|
||||
|
||||
Reference in New Issue
Block a user