use loader + recyclerview for identity list

This commit is contained in:
Vincent Breitmoser
2017-05-29 15:18:42 +02:00
parent e7b9b48d5c
commit 8044586071
7 changed files with 334 additions and 37 deletions

View File

@@ -0,0 +1,111 @@
/*
* 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.util.List;
import android.content.Context;
import android.graphics.Typeface;
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.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter.ViewHolder;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.IdentityInfo;
public class IdentityAdapter extends RecyclerView.Adapter<ViewHolder> {
private final Context context;
private final LayoutInflater layoutInflater;
private List<IdentityInfo> data;
public IdentityAdapter(Context context) {
super();
this.layoutInflater = LayoutInflater.from(context);
this.context = context;
}
public void setData(List<IdentityInfo> data) {
this.data = data;
notifyDataSetChanged();
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
IdentityInfo info = data.get(position);
if (info.name != null) {
holder.vName.setText(info.name);
} 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);
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(layoutInflater.inflate(R.layout.view_key_identity_user_id, null));
}
@Override
public int getItemCount() {
return data != null ? data.size() : 0;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private View v;
private final TextView vName;
private final TextView vAddress;
private final TextView vComment;
public ViewHolder(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);
}
}
}

View File

@@ -28,7 +28,6 @@ import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
@@ -47,7 +46,6 @@ import org.sufficientlysecure.keychain.util.Preferences;
public class ViewKeyFragment extends LoaderFragment implements LinkedIdsFragMvpView, ViewKeyMvpView {
public static final String ARG_MASTER_KEY_ID = "master_key_id";
public static final String ARG_IS_SECRET = "is_secret";

View File

@@ -0,0 +1,151 @@
/*
* 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.loader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.content.AsyncTaskLoader;
import android.util.Log;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.IdentityInfo;
public class IdentityLoader extends AsyncTaskLoader<List<IdentityInfo>> {
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 long masterKeyId;
private List<IdentityInfo> cachedResult;
public IdentityLoader(Context context, ContentResolver contentResolver, long masterKeyId) {
super(context);
this.contentResolver = contentResolver;
this.masterKeyId = masterKeyId;
}
@Override
public List<IdentityInfo> loadInBackground() {
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;
}
try {
ArrayList<IdentityInfo> identities = new ArrayList<>();
while (cursor.moveToNext()) {
IdentityInfo identityInfo = new IdentityInfo(masterKeyId, cursor);
identities.add(identityInfo);
}
return Collections.unmodifiableList(identities);
} finally {
cursor.close();
}
}
@Override
public void deliverResult(List<IdentityInfo> keySubkeyStatus) {
cachedResult = keySubkeyStatus;
if (isStarted()) {
super.deliverResult(keySubkeyStatus);
}
}
@Override
protected void onStartLoading() {
if (cachedResult != null) {
deliverResult(cachedResult);
}
if (takeContentChanged() || cachedResult == null) {
forceLoad();
}
}
public static class IdentityInfo {
final int position;
public final int verified;
public final byte[] data;
public final String name;
public final String email;
public final String comment;
public boolean isPrimary;
IdentityInfo(long masterKeyId, Cursor cursor) {
position = cursor.getPosition();
verified = cursor.getInt(INDEX_VERIFIED);
if (cursor.isNull(INDEX_NAME)) {
data = cursor.getBlob(INDEX_ATTRIBUTE_DATA);
name = null;
email = null;
comment = null;
} else {
data = null;
name = cursor.getString(INDEX_NAME);
email = cursor.getString(INDEX_EMAIL);
comment = cursor.getString(INDEX_COMMENT);
}
isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0;
}
}
}

View File

@@ -18,28 +18,29 @@
package org.sufficientlysecure.keychain.ui.keyview.presenter;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
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.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.EditIdentitiesActivity;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader;
import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityLoader.IdentityInfo;
public class IdentitiesPresenter implements LoaderCallbacks<Cursor> {
public class IdentitiesPresenter implements LoaderCallbacks<List<IdentityInfo>> {
private final Context context;
private final IdentitiesMvpView view;
private final ViewKeyMvpView viewKeyMvpView;
private final int loaderId;
private final UserIdsAdapter userIdsAdapter;
private final IdentityAdapter identitiesAdapter;
private final long masterKeyId;
private final boolean isSecret;
@@ -54,8 +55,8 @@ public class IdentitiesPresenter implements LoaderCallbacks<Cursor> {
this.masterKeyId = masterKeyId;
this.isSecret = isSecret;
userIdsAdapter = new UserIdsAdapter(context, null, 0, !isSecret);
view.setUserIdsAdapter(userIdsAdapter);
identitiesAdapter = new IdentityAdapter(context);
view.setIdentitiesAdapter(identitiesAdapter);
view.setEditIdentitiesButtonVisible(isSecret);
@@ -77,29 +78,29 @@ public class IdentitiesPresenter implements LoaderCallbacks<Cursor> {
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return UserIdsAdapter.createLoader(context, KeyRings.buildUnifiedKeyRingUri(masterKeyId));
public Loader<List<IdentityInfo>> onCreateLoader(int id, Bundle args) {
return new IdentityLoader(context, context.getContentResolver(), masterKeyId);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
public void onLoadFinished(Loader<List<IdentityInfo>> loader, List<IdentityInfo> data) {
viewKeyMvpView.setContentShown(true, false);
userIdsAdapter.swapCursor(data);
identitiesAdapter.setData(data);
}
@Override
public void onLoaderReset(Loader loader) {
userIdsAdapter.swapCursor(null);
identitiesAdapter.setData(null);
}
private void showUserIdInfo(final int position) {
if (!isSecret) {
final boolean isRevoked = userIdsAdapter.getIsRevoked(position);
final int isVerified = userIdsAdapter.getIsVerified(position);
UserIdInfoDialogFragment dialogFragment = UserIdInfoDialogFragment.newInstance(isRevoked, isVerified);
viewKeyMvpView.showDialogFragment(dialogFragment, "userIdInfoDialog");
}
// 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 editIdentities() {
@@ -109,7 +110,7 @@ public class IdentitiesPresenter implements LoaderCallbacks<Cursor> {
}
public interface IdentitiesMvpView {
void setUserIdsAdapter(UserIdsAdapter userIdsAdapter);
void setIdentitiesAdapter(IdentityAdapter userIdsAdapter);
void setIdentitiesCardListener(IdentitiesCardListener identitiesCardListener);
void setEditIdentitiesButtonVisible(boolean show);
}

View File

@@ -20,6 +20,8 @@ package org.sufficientlysecure.keychain.ui.keyview.view;
import android.content.Context;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -29,17 +31,18 @@ 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.adapter.UserIdsAdapter;
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.RecyclerItemClickListener;
public class IdentitiesCardView extends CardView implements IdentitiesMvpView, LinkedIdsMvpView {
private final ListView vLinkedIds;
private final ListView vUserIds;
private final RecyclerView vIdentities;
private final View vLinkedIdsDivider;
private LinkedIdsClickListener linkedIdsClickListener;
@@ -50,7 +53,8 @@ public class IdentitiesCardView extends CardView implements IdentitiesMvpView, L
View view = LayoutInflater.from(context).inflate(R.layout.identities_card, this, true);
vUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
vIdentities = (RecyclerView) view.findViewById(R.id.view_key_user_ids);
vIdentities.setLayoutManager(new LinearLayoutManager(context));
Button userIdsEditButton = (Button) view.findViewById(R.id.view_key_card_user_ids_edit);
userIdsEditButton.setOnClickListener(new OnClickListener() {
@@ -62,14 +66,15 @@ public class IdentitiesCardView extends CardView implements IdentitiesMvpView, L
}
});
vUserIds.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (identitiesCardListener != null) {
identitiesCardListener.onIdentityItemClick(position);
}
}
});
vIdentities.addOnItemTouchListener(new RecyclerItemClickListener(context,
new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
if (identitiesCardListener != null) {
identitiesCardListener.onIdentityItemClick(position);
}
}
}));
vLinkedIds = (ListView) view.findViewById(R.id.view_key_linked_ids);
Button linkedIdsAddButton = (Button) view.findViewById(R.id.view_key_card_linked_ids_add);
@@ -96,8 +101,8 @@ public class IdentitiesCardView extends CardView implements IdentitiesMvpView, L
}
@Override
public void setUserIdsAdapter(UserIdsAdapter userIdsAdapter) {
vUserIds.setAdapter(userIdsAdapter);
public void setIdentitiesAdapter(IdentityAdapter identityAdapter) {
vIdentities.setAdapter(identityAdapter);
}
@Override

View File

@@ -27,7 +27,7 @@
tools:visibility="visible"
/>
<org.sufficientlysecure.keychain.ui.widget.FixedListView
<android.support.v7.widget.RecyclerView
android:id="@+id/view_key_user_ids"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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">
<TextView
android:id="@+id/user_id_item_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android: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"
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"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>