extract linked id loading from ContentProvider

This commit is contained in:
Vincent Breitmoser 2018-06-20 15:40:47 +02:00
parent 9ad29318e8
commit 377bf55b70
13 changed files with 183 additions and 271 deletions

View file

@ -0,0 +1,32 @@
package org.sufficientlysecure.keychain.model;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.UserPacketsModel;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
@AutoValue
public abstract class UserPacket implements UserPacketsModel {
public static final Factory<UserPacket> FACTORY = new Factory<>(AutoValue_UserPacket::new);
public static final SelectUserIdsByMasterKeyIdMapper<UserId> USER_ID_MAPPER =
FACTORY.selectUserIdsByMasterKeyIdMapper(AutoValue_UserPacket_UserId::new);
public static final SelectUserAttributesByTypeAndMasterKeyIdMapper<UserAttribute> USER_ATTRIBUTE_MAPPER =
FACTORY.selectUserAttributesByTypeAndMasterKeyIdMapper(AutoValue_UserPacket_UserAttribute::new);
@AutoValue
public static abstract class UserId implements SelectUserIdsByMasterKeyIdModel {
public boolean isVerified() {
Integer verified = verified();
return verified != null && verified == Certs.VERIFIED_SECRET;
}
}
@AutoValue
public static abstract class UserAttribute implements SelectUserAttributesByTypeAndMasterKeyIdModel {
public boolean isVerified() {
Integer verified = verified();
return verified != null && verified == Certs.VERIFIED_SECRET;
}
}
}

View file

@ -109,7 +109,6 @@ public class KeychainContract {
public static final String PATH_PUBLIC = "public";
public static final String PATH_USER_IDS = "user_ids";
public static final String PATH_LINKED_IDS = "linked_ids";
public static final String PATH_KEYS = "keys";
public static final String PATH_CERTS = "certs";
@ -253,15 +252,6 @@ public class KeychainContract {
public static Uri buildUserIdsUri(Uri uri) {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_USER_IDS).build();
}
public static Uri buildLinkedIdsUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_LINKED_IDS).build();
}
public static Uri buildLinkedIdsUri(Uri uri) {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_LINKED_IDS).build();
}
}
public static class Certs implements CertsColumns, BaseColumns {

View file

@ -25,7 +25,6 @@ import java.util.List;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
@ -39,7 +38,6 @@ import android.text.TextUtils;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.model.AutocryptPeer;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@ -64,7 +62,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
private static final int KEY_RING_USER_IDS = 202;
private static final int KEY_RING_PUBLIC = 203;
private static final int KEY_RING_CERTS = 205;
private static final int KEY_RING_LINKED_IDS = 207;
private static final int KEY_RINGS_FIND_BY_EMAIL = 400;
private static final int KEY_RINGS_FIND_BY_SUBKEY = 401;
@ -140,9 +137,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ KeychainContract.PATH_USER_IDS,
KEY_RING_USER_IDS);
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ KeychainContract.PATH_LINKED_IDS,
KEY_RING_LINKED_IDS);
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ KeychainContract.PATH_PUBLIC,
KEY_RING_PUBLIC);
@ -473,8 +467,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
}
case KEY_RINGS_USER_IDS:
case KEY_RING_USER_IDS:
case KEY_RING_LINKED_IDS: {
case KEY_RING_USER_IDS: {
HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(UserPackets._ID, Tables.USER_PACKETS + ".oid AS _id");
projectionMap.put(UserPackets.MASTER_KEY_ID, Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID);
@ -502,15 +495,10 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
groupBy = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ ", " + Tables.USER_PACKETS + "." + UserPackets.RANK;
if (match == KEY_RING_LINKED_IDS) {
qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " = "
+ WrappedUserAttribute.UAT_URI_ATTRIBUTE);
} else {
qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL");
}
qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL");
// If we are searching for a particular keyring's ids, add where
if (match == KEY_RING_USER_IDS || match == KEY_RING_LINKED_IDS) {
if (match == KEY_RING_USER_IDS) {
qb.appendWhere(" AND ");
qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1));

View file

@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@ -42,6 +43,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
@ -179,7 +181,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
private void showUserIdInfo(final int position) {
final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position);
final int isVerified = mUserIdsAdapter.getIsVerified(position);
final boolean isVerified = mUserIdsAdapter.getIsVerified(position) == Certs.VERIFIED_SECRET;
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {

View file

@ -35,12 +35,11 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.linked.UriAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter.ViewHolder;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.AutocryptPeerInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.UserIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.AutocryptPeerInfo;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker;
@ -158,7 +157,7 @@ public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
public void bind(Context context, LinkedIdInfo info, boolean isSecret) {
bindVerified(context, info, isSecret);
UriAttribute uriAttribute = info.getUriAttribute();
UriAttribute uriAttribute = info.getLinkedAttribute();
bind(context, uriAttribute);
}
@ -178,19 +177,12 @@ public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
private void bindVerified(Context context, IdentityInfo info, boolean isSecret) {
if (!isSecret) {
switch (info.getVerified()) {
case Certs.VERIFIED_SECRET:
KeyFormattingUtils.setStatusImage(context, vVerified,
null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
case Certs.VERIFIED_SELF:
KeyFormattingUtils.setStatusImage(context, vVerified,
null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
default:
KeyFormattingUtils.setStatusImage(context, vVerified,
null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR);
break;
if (info.isVerified()) {
KeyFormattingUtils.setStatusImage(context, vVerified,
null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
} else {
KeyFormattingUtils.setStatusImage(context, vVerified,
null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
}
}
}

View file

@ -17,13 +17,11 @@
package org.sufficientlysecure.keychain.ui.adapter;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.v4.content.CursorLoader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -33,7 +31,6 @@ import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
@ -184,14 +181,4 @@ public class UserIdsAdapter extends UserAttributesAdapter {
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.view_key_adv_user_id_item, null);
}
// don't show revoked user ids, irrelevant for average users
public static final String USER_IDS_WHERE = UserPackets.IS_REVOKED + " = 0";
public static CursorLoader createLoader(Context context, Uri dataUri) {
Uri baseUri = UserPackets.buildUserIdsUri(dataUri);
return new CursorLoader(context, baseUri,
UserIdsAdapter.USER_PACKETS_PROJECTION, USER_IDS_WHERE, null, null);
}
}

View file

@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
@ -24,7 +25,6 @@ import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
public class UserIdInfoDialogFragment extends DialogFragment {
private static final String ARG_IS_REVOKED = "is_revoked";
@ -33,11 +33,11 @@ public class UserIdInfoDialogFragment extends DialogFragment {
/**
* Creates new instance of this dialog fragment
*/
public static UserIdInfoDialogFragment newInstance(boolean isRevoked, int isVerified) {
public static UserIdInfoDialogFragment newInstance(boolean isRevoked, boolean isVerified) {
UserIdInfoDialogFragment frag = new UserIdInfoDialogFragment();
Bundle args = new Bundle();
args.putBoolean(ARG_IS_REVOKED, isRevoked);
args.putInt(ARG_IS_VERIFIED, isVerified);
args.putBoolean(ARG_IS_VERIFIED, isVerified);
frag.setArguments(args);
@ -51,7 +51,7 @@ public class UserIdInfoDialogFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
int isVerified = getArguments().getInt(ARG_IS_VERIFIED);
boolean isVerified = getArguments().getBoolean(ARG_IS_VERIFIED);
boolean isRevoked = getArguments().getBoolean(ARG_IS_REVOKED);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
@ -62,19 +62,12 @@ public class UserIdInfoDialogFragment extends DialogFragment {
title = getString(R.string.user_id_info_revoked_title);
message = getString(R.string.user_id_info_revoked_text);
} else {
switch (isVerified) {
case KeychainContract.Certs.VERIFIED_SECRET:
title = getString(R.string.user_id_info_certified_title);
message = getString(R.string.user_id_info_certified_text);
break;
case KeychainContract.Certs.VERIFIED_SELF:
title = getString(R.string.user_id_info_uncertified_title);
message = getString(R.string.user_id_info_uncertified_text);
break;
default:
title = getString(R.string.user_id_info_invalid_title);
message = getString(R.string.user_id_info_invalid_text);
break;
if (isVerified) {
title = getString(R.string.user_id_info_certified_title);
message = getString(R.string.user_id_info_certified_text);
} else {
title = getString(R.string.user_id_info_uncertified_title);
message = getString(R.string.user_id_info_uncertified_text);
}
}

View file

@ -18,15 +18,12 @@
package org.sufficientlysecure.keychain.ui.keyview;
import java.io.IOException;
import java.util.Collections;
import android.arch.lifecycle.LiveData;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@ -37,10 +34,7 @@ import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentManager.OnBackStackChangedListener;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -62,15 +56,13 @@ import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.keyview.LinkedIdViewFragment.ViewHolder.VerifyState;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.Notify;
@ -81,14 +73,11 @@ import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
import timber.log.Timber;
public class LinkedIdViewFragment extends CryptoOperationFragment implements
LoaderManager.LoaderCallbacks<Cursor>, OnBackStackChangedListener {
public class LinkedIdViewFragment extends CryptoOperationFragment implements OnBackStackChangedListener {
private static final String ARG_DATA_URI = "data_uri";
private static final String ARG_LID_RANK = "rank";
private static final String ARG_IS_SECRET = "verified";
private static final String ARG_MASTER_KEY_ID = "master_key_id";
private static final int LOADER_ID_LINKED_ID = 1;
private long masterKeyId;
private boolean isSecret;
@ -98,16 +87,14 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
private AsyncTask taskInProgress;
private Uri dataUri;
private ViewHolder viewHolder;
private int lidRank;
private long certifyKeyId;
public static LinkedIdViewFragment newInstance(Uri dataUri, int rank, boolean isSecret, long masterKeyId) {
public static LinkedIdViewFragment newInstance(long masterKeyId, int rank, boolean isSecret) {
LinkedIdViewFragment frag = new LinkedIdViewFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_DATA_URI, dataUri);
args.putInt(ARG_LID_RANK, rank);
args.putBoolean(ARG_IS_SECRET, isSecret);
args.putLong(ARG_MASTER_KEY_ID, masterKeyId);
@ -127,57 +114,27 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
super.onCreate(savedInstanceState);
Bundle args = getArguments();
dataUri = args.getParcelable(ARG_DATA_URI);
lidRank = args.getInt(ARG_LID_RANK);
isSecret = args.getBoolean(ARG_IS_SECRET);
masterKeyId = args.getLong(ARG_MASTER_KEY_ID);
getLoaderManager().initLoader(LOADER_ID_LINKED_ID, null, this);
IdentityDao identityDao = IdentityDao.getInstance(getContext());
GenericLiveData<LinkedIdInfo> uriAttributeLiveData =
new GenericLiveData<>(getContext(), null, () -> identityDao.getLinkedIdInfo(masterKeyId, lidRank));
uriAttributeLiveData.observe(this, this::onLinkedIdInfoLoaded);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
case LOADER_ID_LINKED_ID:
return new CursorLoader(getContext(), dataUri,
UserIdsAdapter.USER_PACKETS_PROJECTION,
Tables.USER_PACKETS + "." + UserPackets.RANK
+ " = " + Integer.toString(lidRank), null, null);
default:
return null;
private void onLinkedIdInfoLoaded(LinkedIdInfo linkedIdInfo) {
if (linkedIdInfo == null) {
Timber.e("error loading identity");
Notify.create(getActivity(), "Error loading linked identity!",
Notify.LENGTH_LONG, Style.ERROR).show();
finishFragment();
return;
}
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
switch (loader.getId()) {
case LOADER_ID_LINKED_ID:
// Nothing to load means break if we are *expected* to load
if (!cursor.moveToFirst()) {
// Or just ignore, this is probably some intermediate state during certify
break;
}
try {
int certStatus = cursor.getInt(UserIdsAdapter.INDEX_VERIFIED);
byte[] data = cursor.getBlob(UserIdsAdapter.INDEX_ATTRIBUTE_DATA);
UriAttribute linkedId = LinkedAttribute.fromAttributeData(data);
loadIdentity(linkedId, certStatus);
} catch (IOException e) {
Timber.e(e, "error parsing identity");
Notify.create(getActivity(), "Error parsing identity!",
Notify.LENGTH_LONG, Style.ERROR).show();
finishFragment();
}
break;
}
loadIdentity(linkedIdInfo.getLinkedAttribute(), linkedIdInfo.isVerified());
}
public void finishFragment() {
@ -188,28 +145,19 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
});
}
private void loadIdentity(UriAttribute linkedId, int certStatus) {
private void loadIdentity(LinkedAttribute linkedId, boolean isVerified) {
this.linkedId = linkedId;
if (this.linkedId instanceof LinkedAttribute) {
LinkedResource res = ((LinkedAttribute) this.linkedId).mResource;
linkedResource = (LinkedTokenResource) res;
}
LinkedResource res = ((LinkedAttribute) this.linkedId).mResource;
linkedResource = (LinkedTokenResource) res;
if (!isSecret) {
switch (certStatus) {
case Certs.VERIFIED_SECRET:
KeyFormattingUtils.setStatusImage(getContext(), viewHolder.mLinkedIdHolder.vVerified,
null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
case Certs.VERIFIED_SELF:
KeyFormattingUtils.setStatusImage(getContext(), viewHolder.mLinkedIdHolder.vVerified,
null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
default:
KeyFormattingUtils.setStatusImage(getContext(), viewHolder.mLinkedIdHolder.vVerified,
null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR);
break;
if (isVerified) {
KeyFormattingUtils.setStatusImage(getContext(), viewHolder.mLinkedIdHolder.vVerified,
null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
} else {
KeyFormattingUtils.setStatusImage(getContext(), viewHolder.mLinkedIdHolder.vVerified,
null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
}
} else {
viewHolder.mLinkedIdHolder.vVerified.setImageResource(R.drawable.octo_link_24dp);
@ -219,13 +167,6 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
setShowVerifying(false);
// no resource, nothing further we can do
if (linkedResource == null) {
viewHolder.vButtonView.setVisibility(View.GONE);
viewHolder.vButtonVerify.setVisibility(View.GONE);
return;
}
if (linkedResource.isViewable()) {
viewHolder.vButtonView.setVisibility(View.VISIBLE);
viewHolder.vButtonView.setOnClickListener(v -> {
@ -241,11 +182,6 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
static class ViewHolder {
private final View vButtonView;
private final ViewAnimator vVerifyingContainer;
@ -395,25 +331,29 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.linked_id_view_fragment, null);
View root = inflater.inflate(R.layout.linked_id_view_fragment, superContainer, false);
Context context = getContext();
if (context == null) {
throw new NullPointerException();
}
viewHolder = new ViewHolder(root);
root.setTag(viewHolder);
((ImageView) root.findViewById(R.id.status_icon_verified))
.setColorFilter(ContextCompat.getColor(getContext(), R.color.android_green_light),
.setColorFilter(ContextCompat.getColor(context, R.color.android_green_light),
PorterDuff.Mode.SRC_IN);
((ImageView) root.findViewById(R.id.status_icon_invalid))
.setColorFilter(ContextCompat.getColor(getContext(), R.color.android_red_light),
.setColorFilter(ContextCompat.getColor(context, R.color.android_red_light),
PorterDuff.Mode.SRC_IN);
viewHolder.vButtonVerify.setOnClickListener(v -> verifyResource());
viewHolder.vButtonRetry.setOnClickListener(v -> verifyResource());
viewHolder.vButtonConfirm.setOnClickListener(v -> initiateCertifying());
CertificationDao certificationDao = CertificationDao.getInstance(getContext());
CertificationDao certificationDao = CertificationDao.getInstance(context);
LiveData<CertDetails> certDetailsLiveData = new GenericLiveData<>(
getContext(), null, () -> certificationDao.getVerifyingCertDetails(masterKeyId, lidRank));
context, null, () -> certificationDao.getVerifyingCertDetails(masterKeyId, lidRank));
certDetailsLiveData.observe(this, this::onLoadCertDetails);
return root;

View file

@ -23,7 +23,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.content.ContentResolver;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@ -33,63 +33,40 @@ import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import com.squareup.sqldelight.SqlDelightQuery;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.linked.LinkedAttribute;
import org.sufficientlysecure.keychain.linked.UriAttribute;
import org.sufficientlysecure.keychain.model.AutocryptPeer;
import org.sufficientlysecure.keychain.model.UserPacket;
import org.sufficientlysecure.keychain.model.UserPacket.UserAttribute;
import org.sufficientlysecure.keychain.model.UserPacket.UserId;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDao;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.ui.util.PackageIconGetter;
import timber.log.Timber;
public class IdentityDao {
private static final String[] USER_PACKETS_PROJECTION = new String[]{
UserPackets._ID,
UserPackets.TYPE,
UserPackets.USER_ID,
UserPackets.ATTRIBUTE_DATA,
UserPackets.RANK,
UserPackets.VERIFIED,
UserPackets.IS_PRIMARY,
UserPackets.IS_REVOKED,
UserPackets.NAME,
UserPackets.EMAIL,
UserPackets.COMMENT,
};
private static final int INDEX_ID = 0;
private static final int INDEX_TYPE = 1;
private static final int INDEX_USER_ID = 2;
private static final int INDEX_ATTRIBUTE_DATA = 3;
private static final int INDEX_RANK = 4;
private static final int INDEX_VERIFIED = 5;
private static final int INDEX_IS_PRIMARY = 6;
private static final int INDEX_IS_REVOKED = 7;
private static final int INDEX_NAME = 8;
private static final int INDEX_EMAIL = 9;
private static final int INDEX_COMMENT = 10;
private static final String USER_IDS_WHERE = UserPackets.IS_REVOKED + " = 0";
private final ContentResolver contentResolver;
private final SupportSQLiteDatabase db;
private final PackageIconGetter packageIconGetter;
private final PackageManager packageManager;
private final AutocryptPeerDao autocryptPeerDao;
static IdentityDao getInstance(Context context) {
ContentResolver contentResolver = context.getContentResolver();
public static IdentityDao getInstance(Context context) {
SupportSQLiteDatabase db = new KeychainDatabase(context).getWritableDatabase();
PackageManager packageManager = context.getPackageManager();
PackageIconGetter iconGetter = PackageIconGetter.getInstance(context);
AutocryptPeerDao autocryptPeerDao = AutocryptPeerDao.getInstance(context);
return new IdentityDao(contentResolver, packageManager, iconGetter, autocryptPeerDao);
return new IdentityDao(db, packageManager, iconGetter, autocryptPeerDao);
}
private IdentityDao(ContentResolver contentResolver, PackageManager packageManager, PackageIconGetter iconGetter,
private IdentityDao(SupportSQLiteDatabase db,
PackageManager packageManager, PackageIconGetter iconGetter,
AutocryptPeerDao autocryptPeerDao) {
this.db = db;
this.packageManager = packageManager;
this.contentResolver = contentResolver;
this.packageIconGetter = iconGetter;
this.autocryptPeerDao = autocryptPeerDao;
}
@ -156,73 +133,70 @@ public class IdentityDao {
}
private void loadLinkedIds(ArrayList<IdentityInfo> identities, long masterKeyId) {
Cursor cursor = contentResolver.query(UserPackets.buildLinkedIdsUri(masterKeyId),
USER_PACKETS_PROJECTION, USER_IDS_WHERE, null, null);
if (cursor == null) {
Timber.e("Error loading key items!");
return;
}
try {
SqlDelightQuery query = UserPacket.FACTORY.selectUserAttributesByTypeAndMasterKeyId(
(long) WrappedUserAttribute.UAT_URI_ATTRIBUTE, masterKeyId);
try (Cursor cursor = db.query(query)) {
while (cursor.moveToNext()) {
int rank = cursor.getInt(INDEX_RANK);
int verified = cursor.getInt(INDEX_VERIFIED);
boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0;
UserAttribute userAttribute = UserPacket.USER_ATTRIBUTE_MAPPER.map(cursor);
byte[] data = cursor.getBlob(INDEX_ATTRIBUTE_DATA);
try {
UriAttribute uriAttribute = LinkedAttribute.fromAttributeData(data);
if (uriAttribute instanceof LinkedAttribute) {
LinkedIdInfo identityInfo = LinkedIdInfo.create(rank, verified, isPrimary, uriAttribute);
identities.add(identityInfo);
}
} catch (IOException e) {
Timber.e(e, "Failed parsing uri attribute");
}
LinkedIdInfo linkedIdInfo = parseLinkedIdInfo(userAttribute);
identities.add(linkedIdInfo);
}
} finally {
cursor.close();
}
}
private void loadUserIds(ArrayList<IdentityInfo> identities, long masterKeyId) {
Cursor cursor = contentResolver.query(UserPackets.buildUserIdsUri(masterKeyId),
USER_PACKETS_PROJECTION, USER_IDS_WHERE, null, null);
if (cursor == null) {
Timber.e("Error loading key items!");
return;
public LinkedIdInfo getLinkedIdInfo(long masterKeyId, int rank) {
SqlDelightQuery query = UserPacket.FACTORY.selectSpecificUserAttribute(
(long) WrappedUserAttribute.UAT_URI_ATTRIBUTE, masterKeyId, rank);
try (Cursor cursor = db.query(query)) {
if (cursor.moveToFirst()) {
UserAttribute userAttribute = UserPacket.USER_ATTRIBUTE_MAPPER.map(cursor);
return parseLinkedIdInfo(userAttribute);
}
}
return null;
}
@Nullable
private LinkedIdInfo parseLinkedIdInfo(UserAttribute userAttribute) {
try {
UriAttribute uriAttribute = LinkedAttribute.fromAttributeData(userAttribute.attribute_data());
if (uriAttribute instanceof LinkedAttribute) {
return LinkedIdInfo.create(userAttribute.rank(),
userAttribute.isVerified(), userAttribute.is_primary(), (LinkedAttribute) uriAttribute);
}
} catch (IOException e) {
Timber.e(e, "Failed parsing uri attribute");
}
return null;
}
private void loadUserIds(ArrayList<IdentityInfo> identities, long masterKeyId) {
SqlDelightQuery query = UserPacket.FACTORY.selectUserIdsByMasterKeyId(masterKeyId);
try (Cursor cursor = db.query(query)) {
while (cursor.moveToNext()) {
int rank = cursor.getInt(INDEX_RANK);
int verified = cursor.getInt(INDEX_VERIFIED);
boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0;
UserId userId = UserPacket.USER_ID_MAPPER.map(cursor);
if (!cursor.isNull(INDEX_NAME) || !cursor.isNull(INDEX_EMAIL)) {
String name = cursor.getString(INDEX_NAME);
String email = cursor.getString(INDEX_EMAIL);
String comment = cursor.getString(INDEX_COMMENT);
IdentityInfo identityInfo = UserIdInfo.create(rank, verified, isPrimary, name, email, comment);
if (userId.name() != null || userId.email() != null) {
IdentityInfo identityInfo = UserIdInfo.create(
userId.rank(), userId.isVerified(), userId.is_primary(), userId.name(), userId.email(), userId.comment());
identities.add(identityInfo);
}
}
} finally {
cursor.close();
}
}
public interface IdentityInfo {
int getRank();
int getVerified();
boolean isVerified();
boolean isPrimary();
}
@AutoValue
public abstract static class UserIdInfo implements IdentityInfo {
public abstract int getRank();
public abstract int getVerified();
public abstract boolean isVerified();
public abstract boolean isPrimary();
@Nullable
@ -232,29 +206,29 @@ public class IdentityDao {
@Nullable
public abstract String getComment();
static UserIdInfo create(int rank, int verified, boolean isPrimary, String name, String email,
static UserIdInfo create(int rank, boolean isVerified, boolean isPrimary, String name, String email,
String comment) {
return new AutoValue_IdentityDao_UserIdInfo(rank, verified, isPrimary, name, email, comment);
return new AutoValue_IdentityDao_UserIdInfo(rank, isVerified, isPrimary, name, email, comment);
}
}
@AutoValue
public abstract static class LinkedIdInfo implements IdentityInfo {
public abstract int getRank();
public abstract int getVerified();
public abstract boolean isVerified();
public abstract boolean isPrimary();
public abstract UriAttribute getUriAttribute();
public abstract LinkedAttribute getLinkedAttribute();
static LinkedIdInfo create(int rank, int verified, boolean isPrimary, UriAttribute uriAttribute) {
return new AutoValue_IdentityDao_LinkedIdInfo(rank, verified, isPrimary, uriAttribute);
static LinkedIdInfo create(int rank, boolean isVerified, boolean isPrimary, LinkedAttribute linkedAttribute) {
return new AutoValue_IdentityDao_LinkedIdInfo(rank, isVerified, isPrimary, linkedAttribute);
}
}
@AutoValue
public abstract static class AutocryptPeerInfo implements IdentityInfo {
public abstract int getRank();
public abstract int getVerified();
public abstract boolean isVerified();
public abstract boolean isPrimary();
public abstract String getIdentity();
@ -268,13 +242,13 @@ public class IdentityDao {
static AutocryptPeerInfo create(UserIdInfo userIdInfo, String autocryptPeer, String packageName,
Drawable appIcon, Intent autocryptPeerIntent) {
return new AutoValue_IdentityDao_AutocryptPeerInfo(userIdInfo.getRank(), userIdInfo.getVerified(),
return new AutoValue_IdentityDao_AutocryptPeerInfo(userIdInfo.getRank(), userIdInfo.isVerified(),
userIdInfo.isPrimary(), autocryptPeer, packageName, appIcon, userIdInfo, autocryptPeerIntent);
}
static AutocryptPeerInfo create(String autocryptPeer, String packageName, Drawable appIcon, Intent autocryptPeerIntent) {
return new AutoValue_IdentityDao_AutocryptPeerInfo(
0, Certs.VERIFIED_SELF, false, autocryptPeer, packageName, appIcon, null, autocryptPeerIntent);
0, false, false, autocryptPeer, packageName, appIcon, null, autocryptPeerIntent);
}
}

View file

@ -18,20 +18,17 @@
package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.io.IOException;
import java.util.List;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.view.View;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDao;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter.IdentityClickListener;
import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
@ -114,17 +111,14 @@ public class IdentitiesPresenter implements Observer<List<IdentityInfo>> {
}
private void showLinkedId(final LinkedIdInfo info) {
Uri dataUri = UserPackets.buildLinkedIdsUri(KeyRings.buildGenericKeyRingUri(masterKeyId));
LinkedIdViewFragment frag = LinkedIdViewFragment.newInstance(dataUri, info.getRank(), isSecret, masterKeyId);
LinkedIdViewFragment frag = LinkedIdViewFragment.newInstance(masterKeyId, info.getRank(), isSecret);
viewKeyMvpView.switchToFragment(frag, "linked_id");
}
private void showUserIdInfo(UserIdInfo info) {
if (!isSecret) {
final int isVerified = info.getVerified();
UserIdInfoDialogFragment dialogFragment = UserIdInfoDialogFragment.newInstance(false, isVerified);
UserIdInfoDialogFragment dialogFragment = UserIdInfoDialogFragment.newInstance(false, info.isVerified());
viewKeyMvpView.showDialogFragment(dialogFragment, "userIdInfoDialog");
}
}

View file

@ -727,8 +727,6 @@
<string name="user_id_info_certified_text">"This identity has been confirmed by you."</string>
<string name="user_id_info_uncertified_title">"Not confirmed"</string>
<string name="user_id_info_uncertified_text">"This identity has not been confirmed yet. You cannot be sure if the identity really corresponds to a specific person."</string>
<string name="user_id_info_invalid_title">"Invalid"</string>
<string name="user_id_info_invalid_text">"Something is wrong with this identity!"</string>
<!-- Key trust -->
<string name="key_trust_no_cloud_evidence">"No proof from the Internet on this keys trustworthiness."</string>

View file

@ -1,10 +1,12 @@
import java.lang.Integer;
-- TODO implement. this is only here for reference in SQLDelight
CREATE TABLE IF NOT EXISTS certs(
master_key_id INTEGER NOT NULL,
rank INTEGER NOT NULL,
key_id_certifier INTEGER NOT NULL,
type INTEGER NOT NULL,
verified INTEGER NOT NULL,
verified INTEGER AS Integer NOT NULL,
creation INTEGER NOT NULL,
data BLOB NOT NULL,
PRIMARY KEY(master_key_id, rank, key_id_certifier),

View file

@ -1,15 +1,35 @@
import java.lang.Integer;
CREATE TABLE IF NOT EXISTS user_packets(
master_key_id INTEGER,
master_key_id INTEGER NOT NULL,
rank INTEGER AS Integer NOT NULL,
type INTEGER,
user_id TEXT,
name TEXT,
email TEXT,
comment TEXT,
attribute_data BLOB,
is_primary INTEGER,
is_revoked INTEGER,
rank INTEGER,
is_primary INTEGER AS Boolean NOT NULL,
is_revoked INTEGER AS Boolean NOT NULL,
PRIMARY KEY(master_key_id, rank),
FOREIGN KEY(master_key_id) REFERENCES
keyrings_public(master_key_id) ON DELETE CASCADE
);
);
selectUserIdsByMasterKeyId:
SELECT user_packets.master_key_id, user_packets.rank, user_id, name, email, comment, is_primary, is_revoked, certs.verified
FROM user_packets
LEFT JOIN certs ON ( user_packets.master_key_id = certs.master_key_id AND user_packets.rank = certs.rank AND certs.verified > 0 )
WHERE user_packets.type IS NULL AND user_packets.is_revoked = 0 AND user_packets.master_key_id = ?;
selectUserAttributesByTypeAndMasterKeyId:
SELECT user_packets.master_key_id, user_packets.rank, attribute_data, is_primary, is_revoked, certs.verified
FROM user_packets
LEFT JOIN certs ON ( user_packets.master_key_id = certs.master_key_id AND user_packets.rank = certs.rank AND certs.verified > 0 )
WHERE user_packets.type = ? AND user_packets.is_revoked = 0 AND user_packets.master_key_id = ?;
selectSpecificUserAttribute:
SELECT user_packets.master_key_id, user_packets.rank, attribute_data, is_primary, is_revoked, certs.verified
FROM user_packets
LEFT JOIN certs ON ( user_packets.master_key_id = certs.master_key_id AND user_packets.rank = certs.rank AND certs.verified > 0 )
WHERE user_packets.type = ? AND user_packets.master_key_id = ? AND user_packets.rank = ?;