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:
parent
d67ffb4fbb
commit
b19f7a776f
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
Loading…
Reference in a new issue