Changed all occurrences of StickyListHeaders ListView to RecyclerView.

Implemented SuperSlim in Recyclerview of ViewKeyAdvCertsFragment.
Some changes to the way data is bound to the views in the list.
This commit is contained in:
Tobias Erthal 2016-09-11 16:48:35 +02:00
parent d67ffb4fbb
commit b19f7a776f
8 changed files with 346 additions and 292 deletions

View file

@ -23,7 +23,6 @@ dependencies {
compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0'
compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
compile 'com.splitwise:tokenautocomplete:2.0.7@aar'
compile 'se.emilsjolander:stickylistheaders:2.7.0'
compile 'org.sufficientlysecure:html-textview:1.8'
compile 'org.sufficientlysecure:donations:2.4'
compile 'com.nispok:snackbar:2.11.0'
@ -102,7 +101,6 @@ dependencyVerification {
'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13',
'org.ocpsoft.prettytime:prettytime:ef7098d973ae78b57d1a22dc37d3b8a771bf030301300e24055d676b6cdc5e75',
'com.splitwise:tokenautocomplete:f56239588390f103b270b7c12361d99b06313a5a0410dc7f66e241ac4baf9baa',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
'org.sufficientlysecure:html-textview:206f484fe4178be6c831fe680de558764967e7b56496c4cc7f37f2979a477df6',
'org.sufficientlysecure:donations:96f8197bab26dfe41900d824f10f8f1914519cd62eedb77bdac5b223eccdf0a6',
'com.nispok:snackbar:46b5eb9d630d329e13c2ce00ee9fb115ffb66c23c72cff32ee97eedd76824c6f',

View file

@ -77,6 +77,7 @@ import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
@ -88,8 +89,6 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
static final int REQUEST_ACTION = 1;
static final String ORDER = KeyRings.HAS_ANY_SECRET + " DESC, " + KeyRings.USER_ID + " COLLATE NOCASE ASC";
private static final int REQUEST_DELETE = 2;
private static final int REQUEST_VIEW_KEY = 3;
@ -141,6 +140,10 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
case R.id.menu_key_list_multi_delete: {
long[] keyIds = getAdapter().getSelectedMasterKeyIds();
boolean hasSecret = getAdapter().isAnySecretKeySelected();
System.out.println(Arrays.toString(keyIds));
System.out.println(hasSecret);
Intent intent = new Intent(getActivity(), DeleteKeyDialogActivity.class);
intent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS, keyIds);
intent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, hasSecret);
@ -256,36 +259,11 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
final FragmentActivity activity = getActivity();
activity.setTitle(R.string.app_name);
//mStickyList.setOnItemClickListener(this);
//mStickyList.setAreHeadersSticky(true);
//mStickyList.setDrawingListUnderStickyHeader(false);
//mStickyList.setFastScrollEnabled(true);
/* Adds an empty footer view so that the Floating Action Button won't block content
// in last few rows.
View footer = new View(activity);
int spacing = (int) android.util.TypedValue.applyDimension(
android.util.TypedValue.COMPLEX_UNIT_DIP, 72, getResources().getDisplayMetrics()
);
android.widget.AbsListView.LayoutParams params = new android.widget.AbsListView.LayoutParams(
android.widget.AbsListView.LayoutParams.MATCH_PARENT,
spacing
);
footer.setLayoutParams(params);
//mStickyList.addFooterView(footer, null, false);
*/
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Start out with a progress indicator.
hideList(true);
// this view is made visible if no data is available
// mStickyList.setEmptyView(activity.findViewById(R.id.key_list_empty));
hideList(false);
// click on search button (in empty view) starts query for search string
vSearchContainer = (ViewAnimator) activity.findViewById(R.id.search_container);
@ -336,7 +314,9 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new CursorLoader(getActivity(), uri, KeyAdapter.PROJECTION, null, null, ORDER);
return new CursorLoader(getActivity(), uri,
KeySectionedListAdapter.KeyCursor.PROJECTION, null, null,
KeySectionedListAdapter.KeyCursor.ORDER);
}
@Override

View file

@ -17,7 +17,6 @@
package org.sufficientlysecure.keychain.ui;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@ -25,56 +24,25 @@ import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;
import android.view.LayoutInflater;
import com.tonicartos.superslim.LayoutManager;
import org.openintents.openpgp.util.OpenPgpUtils;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.adapter.CertSectionedListAdapter;
import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerFragment;
import org.sufficientlysecure.keychain.util.Log;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
public class ViewKeyAdvCertsFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener {
public class ViewKeyAdvCertsFragment extends RecyclerFragment<CertSectionedListAdapter>
implements LoaderManager.LoaderCallbacks<Cursor>, CertSectionedListAdapter.CertListListener {
public static final String ARG_DATA_URI = "data_uri";
private StickyListHeadersListView mStickyList;
private CertListAdapter mCertsAdapter;
private Uri mDataUriCerts;
// These are the rows that we will retrieve.
static final String[] CERTS_PROJECTION = new String[]{
KeychainContract.Certs._ID,
KeychainContract.Certs.MASTER_KEY_ID,
KeychainContract.Certs.VERIFIED,
KeychainContract.Certs.TYPE,
KeychainContract.Certs.RANK,
KeychainContract.Certs.KEY_ID_CERTIFIER,
KeychainContract.Certs.USER_ID,
KeychainContract.Certs.SIGNER_UID
};
// sort by our user id,
static final String CERTS_SORT_ORDER =
KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.RANK + " ASC, "
+ KeychainContract.Certs.VERIFIED + " DESC, "
+ KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.TYPE + " DESC, "
+ KeychainContract.Certs.SIGNER_UID + " ASC";
/**
* Creates new instance of this fragment
*/
@ -89,56 +57,39 @@ public class ViewKeyAdvCertsFragment extends LoaderFragment implements
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
View view = inflater.inflate(R.layout.view_key_adv_certs_fragment, getContainer());
mStickyList = (StickyListHeadersListView) view.findViewById(R.id.certs_list);
return root;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.view_key_adv_certs_fragment, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
hideList(false);
Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
if (dataUri == null) {
Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
getActivity().finish();
return;
} else {
mDataUriCerts = KeychainContract.Certs.buildCertsUri(dataUri);
}
loadData(dataUri);
}
CertSectionedListAdapter adapter = new CertSectionedListAdapter(getActivity(), null);
adapter.setCertListListener(this);
private void loadData(Uri dataUri) {
mDataUriCerts = KeychainContract.Certs.buildCertsUri(dataUri);
setAdapter(adapter);
setLayoutManager(new LayoutManager(getActivity()));
mStickyList.setAreHeadersSticky(true);
mStickyList.setDrawingListUnderStickyHeader(false);
mStickyList.setOnItemClickListener(this);
mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
// Create an empty adapter we will use to display the loaded data.
mCertsAdapter = new CertListAdapter(getActivity(), null);
mStickyList.setAdapter(mCertsAdapter);
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
getLoaderManager().initLoader(0, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
setContentShown(false);
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new CursorLoader(getActivity(), mDataUriCerts,
CERTS_PROJECTION, null, null, CERTS_SORT_ORDER);
CertSectionedListAdapter.CertCursor.CERTS_PROJECTION, null, null,
CertSectionedListAdapter.CertCursor.CERTS_SORT_ORDER);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
@ -149,11 +100,13 @@ public class ViewKeyAdvCertsFragment extends LoaderFragment implements
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mCertsAdapter.swapCursor(data);
mStickyList.setAdapter(mCertsAdapter);
getAdapter().swapCursor(CertSectionedListAdapter.CertCursor.wrap(data));
// TODO: maybe show not before both are loaded!
setContentShown(true);
if (isResumed()) {
showList(true);
} else {
showList(false);
}
}
/**
@ -161,183 +114,16 @@ public class ViewKeyAdvCertsFragment extends LoaderFragment implements
* We need to make sure we are no longer using it.
*/
public void onLoaderReset(Loader<Cursor> loader) {
mCertsAdapter.swapCursor(null);
getAdapter().swapCursor(null);
}
/**
* On click on item, start key view activity
*/
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
if (view.getTag(R.id.tag_mki) != null) {
long masterKeyId = (Long) view.getTag(R.id.tag_mki);
long rank = (Long) view.getTag(R.id.tag_rank);
long certifierId = (Long) view.getTag(R.id.tag_certifierId);
public void onClick(long masterKeyId, long signerKeyId, long rank) {
if(masterKeyId != 0L) {
Intent viewIntent = new Intent(getActivity(), ViewCertActivity.class);
viewIntent.setData(KeychainContract.Certs.buildCertsSpecificUri(
masterKeyId, rank, certifierId));
masterKeyId, rank, signerKeyId));
startActivity(viewIntent);
}
}
/**
* Implements StickyListHeadersAdapter from library
*/
private class CertListAdapter extends CursorAdapter implements StickyListHeadersAdapter {
private LayoutInflater mInflater;
private int mIndexMasterKeyId, mIndexUserId, mIndexRank;
private int mIndexSignerKeyId, mIndexSignerUserId;
private int mIndexVerified, mIndexType;
public CertListAdapter(Context context, Cursor c) {
super(context, c, 0);
mInflater = LayoutInflater.from(context);
initIndex(c);
}
@Override
public Cursor swapCursor(Cursor newCursor) {
initIndex(newCursor);
return super.swapCursor(newCursor);
}
/**
* Get column indexes for performance reasons just once in constructor and swapCursor. For a
* performance comparison see http://stackoverflow.com/a/17999582
*
* @param cursor
*/
private void initIndex(Cursor cursor) {
if (cursor != null) {
mIndexMasterKeyId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.MASTER_KEY_ID);
mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.USER_ID);
mIndexRank = cursor.getColumnIndexOrThrow(KeychainContract.Certs.RANK);
mIndexType = cursor.getColumnIndexOrThrow(KeychainContract.Certs.TYPE);
mIndexVerified = cursor.getColumnIndexOrThrow(KeychainContract.Certs.VERIFIED);
mIndexSignerKeyId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.KEY_ID_CERTIFIER);
mIndexSignerUserId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.SIGNER_UID);
}
}
/**
* Bind cursor data to the item list view
* <p/>
* NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method.
* Thus no ViewHolder is required here.
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
// set name and stuff, common to both key types
TextView wSignerKeyId = (TextView) view.findViewById(R.id.signerKeyId);
TextView wSignerName = (TextView) view.findViewById(R.id.signerName);
TextView wSignStatus = (TextView) view.findViewById(R.id.signStatus);
String signerKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), cursor.getLong(mIndexSignerKeyId));
OpenPgpUtils.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexSignerUserId));
if (userId.name != null) {
wSignerName.setText(userId.name);
} else {
wSignerName.setText(R.string.user_id_no_name);
}
wSignerKeyId.setText(signerKeyId);
switch (cursor.getInt(mIndexType)) {
case WrappedSignature.DEFAULT_CERTIFICATION: // 0x10
wSignStatus.setText(R.string.cert_default);
break;
case WrappedSignature.NO_CERTIFICATION: // 0x11
wSignStatus.setText(R.string.cert_none);
break;
case WrappedSignature.CASUAL_CERTIFICATION: // 0x12
wSignStatus.setText(R.string.cert_casual);
break;
case WrappedSignature.POSITIVE_CERTIFICATION: // 0x13
wSignStatus.setText(R.string.cert_positive);
break;
case WrappedSignature.CERTIFICATION_REVOCATION: // 0x30
wSignStatus.setText(R.string.cert_revoke);
break;
}
view.setTag(R.id.tag_mki, cursor.getLong(mIndexMasterKeyId));
view.setTag(R.id.tag_rank, cursor.getLong(mIndexRank));
view.setTag(R.id.tag_certifierId, cursor.getLong(mIndexSignerKeyId));
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.view_key_adv_certs_item, parent, false);
}
/**
* Creates a new header view and binds the section headers to it. It uses the ViewHolder
* pattern. Most functionality is similar to getView() from Android's CursorAdapter.
* <p/>
* NOTE: The variables mDataValid and mCursor are available due to the super class
* CursorAdapter.
*/
@Override
public View getHeaderView(int position, View convertView, ViewGroup parent) {
HeaderViewHolder holder;
if (convertView == null) {
holder = new HeaderViewHolder();
convertView = mInflater.inflate(R.layout.view_key_adv_certs_header, parent, false);
holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text);
holder.count = (TextView) convertView.findViewById(R.id.certs_num);
convertView.setTag(holder);
} else {
holder = (HeaderViewHolder) convertView.getTag();
}
if (!mDataValid) {
// no data available at this point
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
return convertView;
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
// set header text as first char in user id
String userId = mCursor.getString(mIndexUserId);
holder.text.setText(userId);
holder.count.setVisibility(View.GONE);
return convertView;
}
/**
* Header IDs should be static, position=1 should always return the same Id that is.
*/
@Override
public long getHeaderId(int position) {
if (!mDataValid) {
// no data available at this point
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
return -1;
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
// otherwise, return the first character of the name as ID
return mCursor.getInt(mIndexRank);
// sort by the first four characters (should be enough I guess?)
// return ByteBuffer.wrap(userId.getBytes()).asLongBuffer().get(0);
}
class HeaderViewHolder {
TextView text;
TextView count;
}
}
}

View file

@ -0,0 +1,264 @@
package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.HashMap;
import org.openintents.openpgp.util.OpenPgpUtils;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.adapter.SectionCursorAdapter;
public class CertSectionedListAdapter extends SectionCursorAdapter<CertSectionedListAdapter.CertCursor, String,
CertSectionedListAdapter.CertItemViewHolder, CertSectionedListAdapter.CertSectionViewHolder> {
private CertListListener mListener;
public CertSectionedListAdapter(Context context, CertCursor cursor) {
super(context, CertCursor.wrap(cursor), 0);
}
public void setCertListListener(CertListListener listener) {
mListener = listener;
}
@Override
public long getIdFromCursor(CertCursor cursor) {
return cursor.getKeyId();
}
@Override
protected String getSectionFromCursor(CertCursor cursor) throws IllegalStateException {
return cursor.getRawSignerUserId();
}
@Override
protected CertSectionViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
return new CertSectionViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.view_key_adv_certs_header, parent, false));
}
@Override
protected CertItemViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
return new CertItemViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.view_key_adv_certs_item, parent, false));
}
@Override
protected void onBindSectionViewHolder(CertSectionViewHolder holder, String section) {
holder.bind(section);
}
@Override
protected void onBindItemViewHolder(CertItemViewHolder holder, CertCursor cursor) {
holder.bind(cursor);
}
class CertItemViewHolder extends SectionCursorAdapter.ViewHolder implements View.OnClickListener {
private TextView mSignerKeyId;
private TextView mSignerName;
private TextView mSignStatus;
public CertItemViewHolder(View itemView) {
super(itemView);
itemView.setClickable(true);
itemView.setOnClickListener(this);
mSignerName = (TextView) itemView.findViewById(R.id.signerName);
mSignStatus = (TextView) itemView.findViewById(R.id.signStatus);
mSignerKeyId = (TextView) itemView.findViewById(R.id.signerKeyId);
}
public void bind(CertCursor cursor) {
String signerKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(
itemView.getContext(), cursor.getCertifierKeyId());
OpenPgpUtils.UserId userId = cursor.getSignerUserId();
if (userId.name != null) {
mSignerName.setText(userId.name);
} else {
mSignerName.setText(R.string.user_id_no_name);
}
mSignerKeyId.setText(signerKeyId);
switch (cursor.getType()) {
case WrappedSignature.DEFAULT_CERTIFICATION: // 0x10
mSignStatus.setText(R.string.cert_default);
break;
case WrappedSignature.NO_CERTIFICATION: // 0x11
mSignStatus.setText(R.string.cert_none);
break;
case WrappedSignature.CASUAL_CERTIFICATION: // 0x12
mSignStatus.setText(R.string.cert_casual);
break;
case WrappedSignature.POSITIVE_CERTIFICATION: // 0x13
mSignStatus.setText(R.string.cert_positive);
break;
case WrappedSignature.CERTIFICATION_REVOCATION: // 0x30
mSignStatus.setText(R.string.cert_revoke);
break;
}
}
@Override
public void onClick(View v) {
if(mListener != null) {
int index = getCursorPositionWithoutSections(getAdapterPosition());
if (moveCursor(index)) {
CertCursor cursor = getCursor();
mListener.onClick(
cursor.getKeyId(),
cursor.getCertifierKeyId(),
cursor.getRank()
);
}
}
}
}
static class CertSectionViewHolder extends SectionCursorAdapter.ViewHolder {
private TextView mHeaderText;
public CertSectionViewHolder(View itemView) {
super(itemView);
mHeaderText = (TextView) itemView.findViewById(R.id.stickylist_header_text);
}
public void bind(String text) {
mHeaderText.setText(text);
}
}
public static class CertCursor extends CursorWrapper {
public static final String[] CERTS_PROJECTION = new String[]{
KeychainContract.Certs._ID,
KeychainContract.Certs.MASTER_KEY_ID,
KeychainContract.Certs.VERIFIED,
KeychainContract.Certs.TYPE,
KeychainContract.Certs.RANK,
KeychainContract.Certs.KEY_ID_CERTIFIER,
KeychainContract.Certs.USER_ID,
KeychainContract.Certs.SIGNER_UID
};
public static final String CERTS_SORT_ORDER =
KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.RANK + " ASC, "
+ KeychainContract.Certs.VERIFIED + " DESC, "
+ KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.TYPE + " DESC, "
+ KeychainContract.Certs.SIGNER_UID + " ASC";
public static CertCursor wrap(Cursor cursor) {
if(cursor != null) {
return new CertCursor(cursor);
} else {
return null;
}
}
private HashMap<String, Integer> mColumnIndices;
/**
* Creates a cursor wrapper.
*
* @param cursor The underlying cursor to wrap.
*/
private CertCursor(Cursor cursor) {
super(cursor);
mColumnIndices = new HashMap<>(cursor.getColumnCount() * 4 / 3, 0.75f);
}
@Override
public void close() {
mColumnIndices.clear();
super.close();
}
public int getEntryId() {
int index = getColumnIndexOrThrow(KeychainContract.Certs._ID);
return getInt(index);
}
public long getKeyId() {
int index = getColumnIndexOrThrow(KeychainContract.Certs.MASTER_KEY_ID);
return getLong(index);
}
public boolean isVerified() {
int index = getColumnIndexOrThrow(KeychainContract.Certs.VERIFIED);
return getInt(index) > 0;
}
public int getType() {
int index = getColumnIndexOrThrow(KeychainContract.Certs.TYPE);
return getInt(index);
}
public long getRank() {
int index = getColumnIndexOrThrow(KeychainContract.Certs.RANK);
return getLong(index);
}
public long getCertifierKeyId() {
int index = getColumnIndexOrThrow(KeychainContract.Certs.KEY_ID_CERTIFIER);
return getLong(index);
}
public String getRawUserId() {
int index = getColumnIndexOrThrow(KeychainContract.Certs.USER_ID);
return getString(index);
}
public String getRawSignerUserId() {
int index = getColumnIndexOrThrow(KeychainContract.Certs.SIGNER_UID);
return getString(index);
}
public OpenPgpUtils.UserId getUserId() {
return KeyRing.splitUserId(getRawUserId());
}
public OpenPgpUtils.UserId getSignerUserId() {
return KeyRing.splitUserId(getRawSignerUserId());
}
@Override
public int getColumnIndexOrThrow(String colName) {
Integer colIndex = mColumnIndices.get(colName);
if(colIndex == null) {
colIndex = super.getColumnIndexOrThrow(colName);
mColumnIndices.put(colName, colIndex);
} else if (colIndex < 0){
throw new IllegalArgumentException("Could not get column index for name: \"" + colName + "\"");
}
return colIndex;
}
@Override
public int getColumnIndex(String colName) {
Integer colIndex = mColumnIndices.get(colName);
if(colIndex == null) {
colIndex = super.getColumnIndex(colName);
mColumnIndices.put(colName, colIndex);
}
return colIndex;
}
}
public interface CertListListener {
void onClick(long masterKeyId, long signerKeyId, long rank);
}
}

View file

@ -5,7 +5,6 @@ import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
@ -92,7 +91,7 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<KeySectionedLi
}
}
return (KeyCursor) super.swapCursor(cursor);
return super.swapCursor(cursor);
}
public void setKeyListener(KeyListListener listener) {
@ -492,6 +491,9 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<KeySectionedLi
}
public static class KeyCursor extends CursorWrapper {
public static final String ORDER = KeychainContract.KeyRings.HAS_ANY_SECRET
+ " DESC, " + KeychainContract.KeyRings.USER_ID + " COLLATE NOCASE ASC";
public static final String[] PROJECTION = new String[]{
KeychainContract.KeyRings._ID,
KeychainContract.KeyRings.MASTER_KEY_ID,

View file

@ -10,7 +10,6 @@ import android.support.v7.widget.RecyclerView;
import org.sufficientlysecure.keychain.util.Log;
public abstract class CursorAdapter<C extends Cursor> extends RecyclerView.Adapter {
public static final String TAG = "CursorAdapter";
private C mCursor;
@ -202,12 +201,12 @@ public abstract class CursorAdapter<C extends Cursor> extends RecyclerView.Adapt
* If the given new Cursor is the same instance is the previously set
* Cursor, null is also returned.
*/
public Cursor swapCursor(C newCursor) {
public C swapCursor(C newCursor) {
if (newCursor == mCursor) {
return null;
}
Cursor oldCursor = mCursor;
C oldCursor = mCursor;
if (oldCursor != null) {
if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);

View file

@ -26,22 +26,45 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<se.emilsjolander.stickylistheaders.StickyListHeadersListView
<LinearLayout
android:id="@android:id/progress"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/certs_list"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:scrollbarStyle="outsideOverlay" />
android:layout_height="match_parent"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/empty_certs"
android:id="@+id/empty"
android:visibility="gone"
android:layout_gravity="center" />
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center" />
</LinearLayout>
<RelativeLayout
android:id="@android:id/widget_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<android.support.v7.widget.RecyclerView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:clipToPadding="false" />
<TextView
android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/empty_certs"
android:visibility="gone"
android:layout_gravity="center" />
</RelativeLayout>
</FrameLayout>

View file

@ -11,6 +11,7 @@
android:layout_gravity="start|left"
android:text="header text" />
<!--
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -23,5 +24,6 @@
android:layout_marginRight="8dp"
android:visibility="visible"
android:textColor="@android:color/darker_gray" />
-->
</RelativeLayout>