move linked identities into loader structure

This commit is contained in:
Vincent Breitmoser 2017-05-29 17:06:00 +02:00
parent 8044586071
commit 7fc805d70c
13 changed files with 335 additions and 496 deletions

View file

@ -284,6 +284,10 @@ public class KeychainContract {
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();
}

View file

@ -1,6 +1,5 @@
/*
* Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2017 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,29 +20,47 @@ package org.sufficientlysecure.keychain.ui.adapter;
import java.util.List;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
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.IdentityLoader.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.UserIdInfo;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker;
public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
private static final int VIEW_TYPE_USER_ID = 0;
private static final int VIEW_TYPE_LINKED_ID = 1;
private final Context context;
private final LayoutInflater layoutInflater;
private final boolean isSecret;
private List<IdentityInfo> data;
public IdentityAdapter(Context context) {
public IdentityAdapter(Context context, boolean isSecret) {
super();
this.layoutInflater = LayoutInflater.from(context);
this.context = context;
this.isSecret = isSecret;
}
public void setData(List<IdentityInfo> data) {
@ -56,36 +73,37 @@ public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
public void onBindViewHolder(ViewHolder holder, int position) {
IdentityInfo info = data.get(position);
if (info.name != null) {
holder.vName.setText(info.name);
int viewType = getItemViewType(position);
if (viewType == VIEW_TYPE_USER_ID) {
((UserIdViewHolder) holder).bind((UserIdInfo) info);
} else if (viewType == VIEW_TYPE_LINKED_ID) {
((LinkedIdViewHolder) holder).bind(context, (LinkedIdInfo) info, isSecret);
} else {
holder.vName.setText(R.string.user_id_no_name);
}
if (info.email != null) {
holder.vAddress.setText(info.email);
holder.vAddress.setVisibility(View.VISIBLE);
} else {
holder.vAddress.setVisibility(View.GONE);
}
if (info.comment != null) {
holder.vComment.setText(info.comment);
holder.vComment.setVisibility(View.VISIBLE);
} else {
holder.vComment.setVisibility(View.GONE);
}
if (info.isPrimary) {
holder.vName.setTypeface(null, Typeface.BOLD);
holder.vAddress.setTypeface(null, Typeface.BOLD);
} else {
holder.vName.setTypeface(null, Typeface.NORMAL);
holder.vAddress.setTypeface(null, Typeface.NORMAL);
throw new IllegalStateException("unhandled identitytype!");
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(layoutInflater.inflate(R.layout.view_key_identity_user_id, null));
if (viewType == VIEW_TYPE_USER_ID) {
return new UserIdViewHolder(layoutInflater.inflate(R.layout.view_key_identity_user_id, parent, false));
} else if (viewType == VIEW_TYPE_LINKED_ID) {
return new LinkedIdViewHolder(layoutInflater.inflate(R.layout.linked_id_item, parent, false));
} else {
throw new IllegalStateException("unhandled identitytype!");
}
}
@Override
public int getItemViewType(int position) {
IdentityInfo info = data.get(position);
if (info instanceof UserIdInfo) {
return VIEW_TYPE_USER_ID;
} else if (info instanceof LinkedIdInfo) {
return VIEW_TYPE_LINKED_ID;
} else {
throw new IllegalStateException("unhandled identitytype!");
}
}
@Override
@ -93,19 +111,121 @@ public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
return data != null ? data.size() : 0;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private View v;
public IdentityInfo getInfo(int position) {
return data.get(position);
}
abstract static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
}
public static class LinkedIdViewHolder extends ViewHolder {
public final ImageView vVerified;
final private ImageView vIcon;
final private TextView vTitle;
final private TextView vComment;
public LinkedIdViewHolder(View view) {
super(view);
vVerified = (ImageView) view.findViewById(R.id.linked_id_certified_icon);
vIcon = (ImageView) view.findViewById(R.id.linked_id_type_icon);
vTitle = (TextView) view.findViewById(R.id.linked_id_title);
vComment = (TextView) view.findViewById(R.id.linked_id_comment);
}
public void bind(Context context, LinkedIdInfo info, boolean isSecret) {
bindVerified(context, info, isSecret);
UriAttribute uriAttribute = info.getUriAttribute();
bind(context, uriAttribute);
}
public void bind(Context context, UriAttribute uriAttribute) {
vTitle.setText(uriAttribute.getDisplayTitle(context));
String comment = uriAttribute.getDisplayComment(context);
if (comment != null) {
vComment.setVisibility(View.VISIBLE);
vComment.setText(comment);
} else {
vComment.setVisibility(View.GONE);
}
vIcon.setImageResource(uriAttribute.getDisplayIcon());
}
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;
}
}
}
public void seekAttention() {
if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
ObjectAnimator anim = SubtleAttentionSeeker.tintText(vComment, 1000);
anim.setStartDelay(200);
anim.start();
}
}
}
private static class UserIdViewHolder extends ViewHolder {
private final TextView vName;
private final TextView vAddress;
private final TextView vComment;
public ViewHolder(View view) {
private UserIdViewHolder(View view) {
super(view);
vName = (TextView) view.findViewById(R.id.user_id_item_name);
vAddress = (TextView) view.findViewById(R.id.user_id_item_address);
vComment = (TextView) view.findViewById(R.id.user_id_item_comment);
}
public void bind(UserIdInfo info) {
if (info.getName() != null) {
vName.setText(info.getName());
} else {
vName.setText(R.string.user_id_no_name);
}
if (info.getEmail() != null) {
vAddress.setText(info.getEmail());
vAddress.setVisibility(View.VISIBLE);
} else {
vAddress.setVisibility(View.GONE);
}
if (info.getComment() != null) {
vComment.setText(info.getComment());
vComment.setVisibility(View.VISIBLE);
} else {
vComment.setVisibility(View.GONE);
}
if (info.isPrimary()) {
vName.setTypeface(null, Typeface.BOLD);
vAddress.setTypeface(null, Typeface.BOLD);
} else {
vName.setTypeface(null, Typeface.NORMAL);
vAddress.setTypeface(null, Typeface.NORMAL);
}
}
}
}

View file

@ -1,205 +0,0 @@
/*
* Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.adapter;
import java.io.IOException;
import java.util.WeakHashMap;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.support.v4.content.CursorLoader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.linked.LinkedAttribute;
import org.sufficientlysecure.keychain.linked.UriAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.keyview.LinkedIdViewFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker;
import org.sufficientlysecure.keychain.util.FilterCursorWrapper;
import org.sufficientlysecure.keychain.util.Log;
public class LinkedIdsAdapter extends UserAttributesAdapter {
private final boolean mIsSecret;
protected LayoutInflater mInflater;
WeakHashMap<Integer,UriAttribute> mLinkedIdentityCache = new WeakHashMap<>();
public LinkedIdsAdapter(Context context, Cursor c, int flags, boolean isSecret) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
mIsSecret = isSecret;
}
@Override
public Cursor swapCursor(Cursor cursor) {
if (cursor == null) {
return super.swapCursor(null);
}
FilterCursorWrapper filteredCursor = new FilterCursorWrapper(cursor) {
@Override
public boolean isVisible(Cursor cursor) {
UriAttribute id = getItemAtPosition(cursor);
return id instanceof LinkedAttribute;
}
};
return super.swapCursor(filteredCursor);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
if (!mIsSecret) {
int isVerified = cursor.getInt(INDEX_VERIFIED);
switch (isVerified) {
case Certs.VERIFIED_SECRET:
KeyFormattingUtils.setStatusImage(mContext, holder.vVerified,
null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
case Certs.VERIFIED_SELF:
KeyFormattingUtils.setStatusImage(mContext, holder.vVerified,
null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
default:
KeyFormattingUtils.setStatusImage(mContext, holder.vVerified,
null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR);
break;
}
}
UriAttribute id = getItemAtPosition(cursor);
holder.setData(mContext, id);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
view.setTransitionName(id.mUri.toString());
}
}
public UriAttribute getItemAtPosition(Cursor cursor) {
int rank = cursor.getInt(INDEX_RANK);
Log.d(Constants.TAG, "requested rank: " + rank);
UriAttribute ret = mLinkedIdentityCache.get(rank);
if (ret != null) {
Log.d(Constants.TAG, "cached!");
return ret;
}
Log.d(Constants.TAG, "not cached!");
try {
byte[] data = cursor.getBlob(INDEX_ATTRIBUTE_DATA);
ret = LinkedAttribute.fromAttributeData(data);
mLinkedIdentityCache.put(rank, ret);
return ret;
} catch (IOException e) {
Log.e(Constants.TAG, "could not read linked identity subpacket data", e);
return null;
}
}
@Override
public UriAttribute getItem(int position) {
Cursor cursor = getCursor();
cursor.moveToPosition(position);
return getItemAtPosition(cursor);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = mInflater.inflate(R.layout.linked_id_item, null);
ViewHolder holder = new ViewHolder(v);
v.setTag(holder);
return v;
}
// don't show revoked user ids, irrelevant for average users
public static final String LINKED_IDS_WHERE = UserPackets.IS_REVOKED + " = 0";
public static CursorLoader createLoader(Context context, Uri dataUri) {
Uri baseUri = UserPackets.buildLinkedIdsUri(dataUri);
return new CursorLoader(context, baseUri,
UserIdsAdapter.USER_PACKETS_PROJECTION, LINKED_IDS_WHERE, null, null);
}
public LinkedIdViewFragment getLinkedIdFragment(int position, long masterKeyId) throws IOException {
Cursor c = getCursor();
c.moveToPosition(position);
int rank = c.getInt(UserIdsAdapter.INDEX_RANK);
Uri dataUri = UserPackets.buildLinkedIdsUri(KeyRings.buildGenericKeyRingUri(masterKeyId));
return LinkedIdViewFragment.newInstance(dataUri, rank, mIsSecret, masterKeyId);
}
public static class ViewHolder {
final public ImageView vVerified;
final public ImageView vIcon;
final public TextView vTitle;
final public TextView vComment;
public ViewHolder(View view) {
vVerified = (ImageView) view.findViewById(R.id.linked_id_certified_icon);
vIcon = (ImageView) view.findViewById(R.id.linked_id_type_icon);
vTitle = (TextView) view.findViewById(R.id.linked_id_title);
vComment = (TextView) view.findViewById(R.id.linked_id_comment);
}
public void setData(Context context, UriAttribute id) {
vTitle.setText(id.getDisplayTitle(context));
String comment = id.getDisplayComment(context);
if (comment != null) {
vComment.setVisibility(View.VISIBLE);
vComment.setText(comment);
} else {
vComment.setVisibility(View.GONE);
}
vIcon.setImageResource(id.getDisplayIcon());
}
public void seekAttention() {
if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
ObjectAnimator anim = SubtleAttentionSeeker.tintText(vComment, 1000);
anim.setStartDelay(200);
anim.start();
}
}
}
}

View file

@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui.keyview;
import java.io.IOException;
import java.util.Collections;
@ -50,11 +51,11 @@ import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.key;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult;
import org.sufficientlysecure.keychain.linked.LinkedTokenResource;
import org.sufficientlysecure.keychain.linked.LinkedAttribute;
import org.sufficientlysecure.keychain.linked.LinkedResource;
import org.sufficientlysecure.keychain.linked.LinkedTokenResource;
import org.sufficientlysecure.keychain.linked.UriAttribute;
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;
@ -63,7 +64,7 @@ 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.LinkedIdsAdapter;
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;
@ -217,7 +218,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
mViewHolder.mLinkedIdHolder.vVerified.setImageResource(R.drawable.octo_link_24dp);
}
mViewHolder.mLinkedIdHolder.setData(mContext, mLinkedId);
mViewHolder.mLinkedIdHolder.bind(mContext, mLinkedId);
setShowVerifying(false);
@ -256,7 +257,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
private final ViewAnimator vVerifyingContainer;
private final ViewAnimator vItemCertified;
private final View vKeySpinnerContainer;
LinkedIdsAdapter.ViewHolder mLinkedIdHolder;
IdentityAdapter.LinkedIdViewHolder mLinkedIdHolder;
private ViewAnimator vButtonSwitcher;
private CertListWidget vLinkedCerts;
@ -274,7 +275,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
vKeySpinnerContainer = root.findViewById(R.id.cert_key_spincontainer);
vButtonSwitcher = (ViewAnimator) root.findViewById(R.id.button_animator);
mLinkedIdHolder = new LinkedIdsAdapter.ViewHolder(root);
mLinkedIdHolder = new IdentityAdapter.LinkedIdViewHolder(root);
vButtonVerify = root.findViewById(R.id.button_verify);
vButtonRetry = root.findViewById(R.id.button_retry);

View file

@ -35,30 +35,25 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.ui.base.LoaderFragment;
import org.sufficientlysecure.keychain.ui.keyview.presenter.IdentitiesPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.KeyHealthPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.LinkedIdentitiesPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.LinkedIdentitiesPresenter.LinkedIdsFragMvpView;
import org.sufficientlysecure.keychain.ui.keyview.presenter.SystemContactPresenter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.ViewKeyMvpView;
import org.sufficientlysecure.keychain.ui.keyview.view.IdentitiesCardView;
import org.sufficientlysecure.keychain.ui.keyview.view.KeyHealthView;
import org.sufficientlysecure.keychain.ui.keyview.view.SystemContactCardView;
import org.sufficientlysecure.keychain.util.Preferences;
public class ViewKeyFragment extends LoaderFragment implements LinkedIdsFragMvpView, ViewKeyMvpView {
public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView {
public static final String ARG_MASTER_KEY_ID = "master_key_id";
public static final String ARG_IS_SECRET = "is_secret";
boolean mIsSecret = false;
private static final int LOADER_ID_USER_IDS = 1;
private static final int LOADER_ID_LINKED_IDS = 2;
private static final int LOADER_ID_LINKED_CONTACT = 3;
private static final int LOADER_ID_SUBKEY_STATUS = 4;
private static final int LOADER_IDENTITIES = 1;
private static final int LOADER_ID_LINKED_CONTACT = 2;
private static final int LOADER_ID_SUBKEY_STATUS = 3;
private IdentitiesCardView mIdentitiesCardView;
private IdentitiesPresenter mIdentitiesPresenter;
private LinkedIdentitiesPresenter mLinkedIdentitiesPresenter;
SystemContactCardView mSystemContactCard;
SystemContactPresenter mSystemContactPresenter;
@ -102,15 +97,9 @@ public class ViewKeyFragment extends LoaderFragment implements LinkedIdsFragMvpV
mIsSecret = getArguments().getBoolean(ARG_IS_SECRET);
mIdentitiesPresenter = new IdentitiesPresenter(
getContext(), mIdentitiesCardView, this, LOADER_ID_USER_IDS, masterKeyId, mIsSecret);
getContext(), mIdentitiesCardView, this, LOADER_IDENTITIES, masterKeyId, mIsSecret);
mIdentitiesPresenter.startLoader(getLoaderManager());
if (Preferences.getPreferences(getActivity()).getExperimentalEnableLinkedIdentities()) {
mLinkedIdentitiesPresenter = new LinkedIdentitiesPresenter(
getContext(), mIdentitiesCardView, this, LOADER_ID_LINKED_IDS, masterKeyId, mIsSecret);
mLinkedIdentitiesPresenter.startLoader(getLoaderManager());
}
mSystemContactPresenter = new SystemContactPresenter(
getContext(), mSystemContactCard, LOADER_ID_LINKED_CONTACT, masterKeyId, mIsSecret);
mSystemContactPresenter.startLoader(getLoaderManager());

View file

@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui.keyview.loader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -25,10 +26,14 @@ import java.util.List;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.support.annotation.Nullable;
import android.support.v4.content.AsyncTaskLoader;
import android.util.Log;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.linked.LinkedAttribute;
import org.sufficientlysecure.keychain.linked.UriAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.IdentityInfo;
@ -63,34 +68,84 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
private final ContentResolver contentResolver;
private final long masterKeyId;
private final boolean showLinkedIds;
private List<IdentityInfo> cachedResult;
public IdentityLoader(Context context, ContentResolver contentResolver, long masterKeyId) {
public IdentityLoader(Context context, ContentResolver contentResolver, long masterKeyId, boolean showLinkedIds) {
super(context);
this.contentResolver = contentResolver;
this.masterKeyId = masterKeyId;
this.showLinkedIds = showLinkedIds;
}
@Override
public List<IdentityInfo> loadInBackground() {
ArrayList<IdentityInfo> identities = new ArrayList<>();
if (showLinkedIds) {
loadLinkedIds(identities);
}
loadUserIds(identities);
return Collections.unmodifiableList(identities);
}
private void loadLinkedIds(ArrayList<IdentityInfo> identities) {
Cursor cursor = contentResolver.query(UserPackets.buildLinkedIdsUri(masterKeyId),
USER_PACKETS_PROJECTION, USER_IDS_WHERE, null, null);
if (cursor == null) {
Log.e(Constants.TAG, "Error loading key items!");
return;
}
try {
while (cursor.moveToNext()) {
int rank = cursor.getInt(INDEX_RANK);
int verified = cursor.getInt(INDEX_VERIFIED);
boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0;
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) {
Log.e(Constants.TAG, "Failed parsing uri attribute", e);
}
}
} finally {
cursor.close();
}
}
private void loadUserIds(ArrayList<IdentityInfo> identities) {
Cursor cursor = contentResolver.query(UserPackets.buildUserIdsUri(masterKeyId),
USER_PACKETS_PROJECTION, USER_IDS_WHERE, null, null);
if (cursor == null) {
Log.e(Constants.TAG, "Error loading key items!");
return null;
return;
}
try {
ArrayList<IdentityInfo> identities = new ArrayList<>();
while (cursor.moveToNext()) {
IdentityInfo identityInfo = new IdentityInfo(masterKeyId, cursor);
identities.add(identityInfo);
}
int rank = cursor.getInt(INDEX_RANK);
int verified = cursor.getInt(INDEX_VERIFIED);
boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0;
return Collections.unmodifiableList(identities);
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);
identities.add(identityInfo);
}
}
} finally {
cursor.close();
}
@ -116,36 +171,41 @@ public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
}
}
public static class IdentityInfo {
final int position;
public interface IdentityInfo {
int getRank();
int getVerified();
boolean isPrimary();
}
public final int verified;
public final byte[] data;
public final String name;
public final String email;
public final String comment;
@AutoValue
public abstract static class UserIdInfo implements IdentityInfo {
public abstract int getRank();
public abstract int getVerified();
public abstract boolean isPrimary();
public boolean isPrimary;
@Nullable
public abstract String getName();
@Nullable
public abstract String getEmail();
@Nullable
public abstract String getComment();
IdentityInfo(long masterKeyId, Cursor cursor) {
position = cursor.getPosition();
static UserIdInfo create(int rank, int verified, boolean isPrimary, String name, String email,
String comment) {
return new AutoValue_IdentityLoader_UserIdInfo(rank, verified, isPrimary, name, email, comment);
}
}
verified = cursor.getInt(INDEX_VERIFIED);
if (cursor.isNull(INDEX_NAME)) {
data = cursor.getBlob(INDEX_ATTRIBUTE_DATA);
@AutoValue
public abstract static class LinkedIdInfo implements IdentityInfo {
public abstract int getRank();
public abstract int getVerified();
public abstract boolean isPrimary();
name = null;
email = null;
comment = null;
} else {
data = null;
public abstract UriAttribute getUriAttribute();
name = cursor.getString(INDEX_NAME);
email = cursor.getString(INDEX_EMAIL);
comment = cursor.getString(INDEX_COMMENT);
}
isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0;
static LinkedIdInfo create(int rank, int verified, boolean isPrimary, UriAttribute uriAttribute) {
return new AutoValue_IdentityLoader_LinkedIdInfo(rank, verified, isPrimary, uriAttribute);
}
}
}

View file

@ -18,20 +18,32 @@
package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.EditIdentitiesActivity;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter;
import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
import org.sufficientlysecure.keychain.ui.keyview.LinkedIdViewFragment;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.IdentityInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.LinkedIdInfo;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.UserIdInfo;
import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>> {
@ -55,7 +67,7 @@ public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>>
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
identitiesAdapter = new IdentityAdapter(context);
identitiesAdapter = new IdentityAdapter(context, isSecret);
view.setIdentitiesAdapter(identitiesAdapter);
view.setEditIdentitiesButtonVisible(isSecret);
@ -63,13 +75,18 @@ public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>>
view.setIdentitiesCardListener(new IdentitiesCardListener() {
@Override
public void onIdentityItemClick(int position) {
showUserIdInfo(position);
showIdentityInfo(position);
}
@Override
public void onClickEditIdentities() {
editIdentities();
}
@Override
public void onClickAddIdentity() {
addLinkedIdentity();
}
});
}
@ -79,7 +96,8 @@ public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>>
@Override
public Loader<List<IdentityInfo>> onCreateLoader(int id, Bundle args) {
return new IdentityLoader(context, context.getContentResolver(), masterKeyId);
boolean showLinkedIds = Preferences.getPreferences(context).getExperimentalEnableLinkedIdentities();
return new IdentityLoader(context, context.getContentResolver(), masterKeyId, showLinkedIds);
}
@Override
@ -93,14 +111,35 @@ public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>>
identitiesAdapter.setData(null);
}
private void showUserIdInfo(final int position) {
// if (!isSecret) {
// final boolean isRevoked = identitiesAdapter.getIsRevoked(position);
// final int isVerified = identitiesAdapter.getIsVerified(position);
//
// UserIdInfoDialogFragment dialogFragment = UserIdInfoDialogFragment.newInstance(isRevoked, isVerified);
// viewKeyMvpView.showDialogFragment(dialogFragment, "userIdInfoDialog");
// }
private void showIdentityInfo(final int position) {
IdentityInfo info = identitiesAdapter.getInfo(position);
if (info instanceof LinkedIdInfo) {
showLinkedId((LinkedIdInfo) info);
} else if (info instanceof UserIdInfo) {
showUserIdInfo((UserIdInfo) info);
}
}
private void showLinkedId(final LinkedIdInfo info) {
final LinkedIdViewFragment frag;
try {
Uri dataUri = UserPackets.buildLinkedIdsUri(KeyRings.buildGenericKeyRingUri(masterKeyId));
frag = LinkedIdViewFragment.newInstance(dataUri, info.getRank(), isSecret, masterKeyId);
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
return;
}
viewKeyMvpView.switchToFragment(frag, "linked_id");
}
private void showUserIdInfo(UserIdInfo info) {
if (!isSecret) {
final int isVerified = info.getVerified();
UserIdInfoDialogFragment dialogFragment = UserIdInfoDialogFragment.newInstance(false, isVerified);
viewKeyMvpView.showDialogFragment(dialogFragment, "userIdInfoDialog");
}
}
private void editIdentities() {
@ -109,6 +148,12 @@ public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>>
viewKeyMvpView.startActivityAndShowResultSnackbar(editIntent);
}
private void addLinkedIdentity() {
Intent intent = new Intent(context, LinkedIdWizard.class);
intent.setData(KeyRings.buildUnifiedKeyRingUri(masterKeyId));
context.startActivity(intent);
}
public interface IdentitiesMvpView {
void setIdentitiesAdapter(IdentityAdapter userIdsAdapter);
void setIdentitiesCardListener(IdentitiesCardListener identitiesCardListener);
@ -117,6 +162,8 @@ public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>>
public interface IdentitiesCardListener {
void onIdentityItemClick(int position);
void onClickEditIdentities();
void onClickAddIdentity();
}
}

View file

@ -1,133 +0,0 @@
/*
* Copyright (C) 2017 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.io.IOException;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter;
import org.sufficientlysecure.keychain.ui.keyview.LinkedIdViewFragment;
import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard;
import org.sufficientlysecure.keychain.util.Log;
public class LinkedIdentitiesPresenter implements LoaderCallbacks<Cursor> {
private final Context context;
private final LinkedIdsMvpView view;
private final int loaderId;
private final LinkedIdsFragMvpView fragView;
private LinkedIdsAdapter linkedIdsAdapter;
private final long masterKeyId;
private final boolean isSecret;
public LinkedIdentitiesPresenter(
Context context, LinkedIdsMvpView view, LinkedIdsFragMvpView fragView, int loaderId, long masterKeyId, boolean isSecret) {
this.context = context;
this.view = view;
this.fragView = fragView;
this.loaderId = loaderId;
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
linkedIdsAdapter = new LinkedIdsAdapter(context, null, 0, isSecret);
view.setLinkedIdsAdapter(linkedIdsAdapter);
view.setSystemContactClickListener(new LinkedIdsClickListener() {
@Override
public void onLinkedIdItemClick(int position) {
showLinkedId(position);
}
@Override
public void onClickAddIdentity() {
addLinkedIdentity();
}
});
}
public void startLoader(LoaderManager loaderManager) {
loaderManager.restartLoader(loaderId, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return LinkedIdsAdapter.createLoader(context, KeyRings.buildUnifiedKeyRingUri(masterKeyId));
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
linkedIdsAdapter.swapCursor(data);
boolean anyDataShown = data.getCount() > 0;
view.setShowLinkedIdDivider(anyDataShown);
}
@Override
public void onLoaderReset(Loader loader) {
linkedIdsAdapter.swapCursor(null);
view.setShowLinkedIdDivider(false);
}
private void showLinkedId(final int position) {
final LinkedIdViewFragment frag;
try {
frag = linkedIdsAdapter.getLinkedIdFragment(position, masterKeyId);
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
return;
}
fragView.switchToFragment(frag, "linked_id");
}
public interface LinkedIdsMvpView {
void setSystemContactClickListener(LinkedIdsClickListener linkedIdsClickListener);
void setLinkedIdsAdapter(LinkedIdsAdapter linkedIdsAdapter);
void setShowLinkedIdDivider(boolean show);
}
public interface LinkedIdsFragMvpView {
void switchToFragment(Fragment frag, String backStackName);
}
public interface LinkedIdsClickListener {
void onLinkedIdItemClick(int position);
void onClickAddIdentity();
}
private void addLinkedIdentity() {
Intent intent = new Intent(context, LinkedIdWizard.class);
intent.setData(KeyRings.buildUnifiedKeyRingUri(masterKeyId));
context.startActivity(intent);
}
}

View file

@ -3,9 +3,12 @@ package org.sufficientlysecure.keychain.ui.keyview.presenter;
import android.content.Intent;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
public interface ViewKeyMvpView {
void switchToFragment(Fragment frag, String backStackName);
void startActivityAndShowResultSnackbar(Intent intent);
void showDialogFragment(DialogFragment dialogFragment, final String tag);
void setContentShown(boolean show, boolean animate);

View file

@ -25,27 +25,19 @@ import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter;
import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter;
import org.sufficientlysecure.keychain.ui.keyview.presenter.IdentitiesPresenter.IdentitiesCardListener;
import org.sufficientlysecure.keychain.ui.keyview.presenter.IdentitiesPresenter.IdentitiesMvpView;
import org.sufficientlysecure.keychain.ui.keyview.presenter.LinkedIdentitiesPresenter.LinkedIdsClickListener;
import org.sufficientlysecure.keychain.ui.keyview.presenter.LinkedIdentitiesPresenter.LinkedIdsMvpView;
import org.sufficientlysecure.keychain.ui.util.recyclerview.DividerItemDecoration;
import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerItemClickListener;
public class IdentitiesCardView extends CardView implements IdentitiesMvpView, LinkedIdsMvpView {
private final ListView vLinkedIds;
public class IdentitiesCardView extends CardView implements IdentitiesMvpView {
private final RecyclerView vIdentities;
private final View vLinkedIdsDivider;
private LinkedIdsClickListener linkedIdsClickListener;
private IdentitiesCardListener identitiesCardListener;
public IdentitiesCardView(Context context, AttributeSet attrs) {
@ -55,6 +47,7 @@ public class IdentitiesCardView extends CardView implements IdentitiesMvpView, L
vIdentities = (RecyclerView) view.findViewById(R.id.view_key_user_ids);
vIdentities.setLayoutManager(new LinearLayoutManager(context));
vIdentities.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL_LIST));
Button userIdsEditButton = (Button) view.findViewById(R.id.view_key_card_user_ids_edit);
userIdsEditButton.setOnClickListener(new OnClickListener() {
@ -76,28 +69,16 @@ public class IdentitiesCardView extends CardView implements IdentitiesMvpView, L
}
}));
vLinkedIds = (ListView) view.findViewById(R.id.view_key_linked_ids);
Button linkedIdsAddButton = (Button) view.findViewById(R.id.view_key_card_linked_ids_add);
linkedIdsAddButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (linkedIdsClickListener != null) {
linkedIdsClickListener.onClickAddIdentity();
if (identitiesCardListener != null) {
identitiesCardListener.onClickAddIdentity();
}
}
});
vLinkedIds.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (linkedIdsClickListener != null) {
linkedIdsClickListener.onLinkedIdItemClick(position);
}
}
});
vLinkedIdsDivider = view.findViewById(R.id.view_key_lid_divider);
}
@Override
@ -114,19 +95,4 @@ public class IdentitiesCardView extends CardView implements IdentitiesMvpView, L
public void setEditIdentitiesButtonVisible(boolean show) {
findViewById(R.id.view_key_card_user_ids_buttons).setVisibility(show ? View.VISIBLE : View.GONE);
}
@Override
public void setSystemContactClickListener(LinkedIdsClickListener linkedIdsClickListener) {
this.linkedIdsClickListener = linkedIdsClickListener;
}
@Override
public void setLinkedIdsAdapter(LinkedIdsAdapter linkedIdsAdapter) {
vLinkedIds.setAdapter(linkedIdsAdapter);
}
@Override
public void setShowLinkedIdDivider(boolean show) {
vLinkedIdsDivider.setVisibility(show ? View.VISIBLE : View.GONE);
}
}

View file

@ -67,7 +67,7 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
for (int i = 0; i < childCount -1; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
@ -83,7 +83,7 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration {
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
for (int i = 0; i < childCount -1; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();

View file

@ -12,21 +12,6 @@
android:layout_height="wrap_content"
android:text="@string/section_user_ids" />
<org.sufficientlysecure.keychain.ui.widget.FixedListView
android:id="@+id/view_key_linked_ids"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp" />
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:id="@+id/view_key_lid_divider"
android:background="?android:attr/listDivider"
android:visibility="gone"
tools:visibility="visible"
/>
<android.support.v7.widget.RecyclerView
android:id="@+id/view_key_user_ids"
android:layout_width="match_parent"

View file

@ -1,31 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp">
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingLeft="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<TextView
android:id="@+id/user_id_item_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="alice@example.com"
tools:text="alice@example.com"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/user_id_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Alice"
tools:text="Alice"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/user_id_item_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="comment"
tools:text="comment"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>