Start implementing multi-select and action modes. Needs testing...
This commit is contained in:
@@ -83,11 +83,13 @@ import java.util.ArrayList;
|
|||||||
* StickyListHeaders library which does not extend upon ListView.
|
* StickyListHeaders library which does not extend upon ListView.
|
||||||
*/
|
*/
|
||||||
public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
||||||
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
implements SearchView.OnQueryTextListener,
|
||||||
LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
|
LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
|
||||||
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||||
|
|
||||||
static final int REQUEST_ACTION = 1;
|
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_DELETE = 2;
|
||||||
private static final int REQUEST_VIEW_KEY = 3;
|
private static final int REQUEST_VIEW_KEY = 3;
|
||||||
|
|
||||||
@@ -108,6 +110,101 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
// for ConsolidateOperation
|
// for ConsolidateOperation
|
||||||
private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
|
private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
|
||||||
|
|
||||||
|
// Callbacks related to listview and menu events
|
||||||
|
private final ActionMode.Callback mActionCallback
|
||||||
|
= new ActionMode.Callback() {
|
||||||
|
@Override
|
||||||
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||||
|
getActivity().getMenuInflater().inflate(R.menu.key_list_multi, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_key_list_multi_encrypt: {
|
||||||
|
long[] keyIds = getAdapter().getSelectedMasterKeyIds();
|
||||||
|
Intent intent = new Intent(getActivity(), EncryptFilesActivity.class);
|
||||||
|
intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA);
|
||||||
|
intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, keyIds);
|
||||||
|
|
||||||
|
startActivityForResult(intent, REQUEST_ACTION);
|
||||||
|
mode.finish();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case R.id.menu_key_list_multi_delete: {
|
||||||
|
long[] keyIds = getAdapter().getSelectedMasterKeyIds();
|
||||||
|
boolean hasSecret = getAdapter().isAnySecretKeySelected();
|
||||||
|
Intent intent = new Intent(getActivity(), DeleteKeyDialogActivity.class);
|
||||||
|
intent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS, keyIds);
|
||||||
|
intent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, hasSecret);
|
||||||
|
if (hasSecret) {
|
||||||
|
intent.putExtra(DeleteKeyDialogActivity.EXTRA_KEYSERVER,
|
||||||
|
Preferences.getPreferences(getActivity()).getPreferredKeyserver());
|
||||||
|
}
|
||||||
|
|
||||||
|
startActivityForResult(intent, REQUEST_DELETE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
|
mActionMode = null;
|
||||||
|
if(getAdapter() != null) {
|
||||||
|
getAdapter().finishSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final KeySectionedListAdapter.KeyListListener mKeyListener
|
||||||
|
= new KeySectionedListAdapter.KeyListListener() {
|
||||||
|
@Override
|
||||||
|
public void onKeyDummyItemClicked() {
|
||||||
|
createKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onKeyItemClicked(long masterKeyId) {
|
||||||
|
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
|
||||||
|
viewIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
|
||||||
|
startActivityForResult(viewIntent, REQUEST_VIEW_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSlingerButtonClicked(long masterKeyId) {
|
||||||
|
Intent safeSlingerIntent = new Intent(getActivity(), SafeSlingerActivity.class);
|
||||||
|
safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
|
||||||
|
startActivityForResult(safeSlingerIntent, REQUEST_ACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectionStateChanged(int selectedCount) {
|
||||||
|
if(selectedCount < 1) {
|
||||||
|
if(mActionMode != null) {
|
||||||
|
mActionMode.finish();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(mActionMode == null) {
|
||||||
|
mActionMode = getActivity().startActionMode(mActionCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
String keysSelected = getResources().getQuantityString(
|
||||||
|
R.plurals.key_list_selected_keys, selectedCount, selectedCount);
|
||||||
|
mActionMode.setTitle(keysSelected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load custom layout with StickyListView from library
|
* Load custom layout with StickyListView from library
|
||||||
*/
|
*/
|
||||||
@@ -181,73 +278,6 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
//mStickyList.addFooterView(footer, null, false);
|
//mStickyList.addFooterView(footer, null, false);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* Multi-selection
|
|
||||||
*/
|
|
||||||
//mStickyList.setFastScrollAlwaysVisible(true);
|
|
||||||
|
|
||||||
//mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
|
||||||
android.view.MenuInflater inflater = activity.getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.key_list_multi, menu);
|
|
||||||
mActionMode = mode;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
|
||||||
|
|
||||||
// get IDs for checked positions as long array
|
|
||||||
long[] ids;
|
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.menu_key_list_multi_encrypt: {
|
|
||||||
ids = mAdapter.getCurrentSelectedMasterKeyIds();
|
|
||||||
encrypt(mode, ids);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case R.id.menu_key_list_multi_delete: {
|
|
||||||
ids = mAdapter.getCurrentSelectedMasterKeyIds();
|
|
||||||
showDeleteKeyDialog(ids, mAdapter.isAnySecretSelected());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyActionMode(ActionMode mode) {
|
|
||||||
mActionMode = null;
|
|
||||||
mAdapter.clearSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
|
|
||||||
boolean checked) {
|
|
||||||
if (checked) {
|
|
||||||
mAdapter.setNewSelection(position, true);
|
|
||||||
} else {
|
|
||||||
mAdapter.removeSelection(position);
|
|
||||||
}
|
|
||||||
int count = mStickyList.getCheckedItemCount();
|
|
||||||
String keysSelected = getResources().getQuantityString(
|
|
||||||
R.plurals.key_list_selected_keys, count, count);
|
|
||||||
mode.setTitle(keysSelected);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
// We have a menu item to show in action bar.
|
// We have a menu item to show in action bar.
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
@@ -270,7 +300,10 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
// Create an empty adapter we will use to display the loaded data.
|
// Create an empty adapter we will use to display the loaded data.
|
||||||
//mAdapter = new KeyListAdapter(activity, null, 0);
|
//mAdapter = new KeyListAdapter(activity, null, 0);
|
||||||
|
|
||||||
setAdapter(new KeySectionedListAdapter(getContext(), null));
|
KeySectionedListAdapter adapter = new KeySectionedListAdapter(getContext(), null);
|
||||||
|
adapter.setKeyListener(mKeyListener);
|
||||||
|
|
||||||
|
setAdapter(adapter);
|
||||||
setLayoutManager(new LayoutManager(getActivity()));
|
setLayoutManager(new LayoutManager(getActivity()));
|
||||||
|
|
||||||
// Prepare the loader. Either re-connect with an existing one,
|
// Prepare the loader. Either re-connect with an existing one,
|
||||||
@@ -290,9 +323,6 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
startActivity(searchIntent);
|
startActivity(searchIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
static final String ORDER =
|
|
||||||
KeyRings.HAS_ANY_SECRET + " DESC, " + KeyRings.USER_ID + " COLLATE NOCASE ASC";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
// This is called when a new Loader needs to be created. This
|
// This is called when a new Loader needs to be created. This
|
||||||
@@ -314,24 +344,7 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
// Swap the new cursor in. (The framework will take care of closing the
|
// Swap the new cursor in. (The framework will take care of closing the
|
||||||
// old cursor once we return.)
|
// old cursor once we return.)
|
||||||
getAdapter().setSearchQuery(mQuery);
|
getAdapter().setSearchQuery(mQuery);
|
||||||
|
getAdapter().swapCursor(KeySectionedListAdapter.KeyCursor.wrap(data));
|
||||||
if (data != null && (mQuery == null || TextUtils.isEmpty(mQuery))) {
|
|
||||||
boolean isSecret = data.moveToFirst() && data.getInt(KeyAdapter.INDEX_HAS_ANY_SECRET) != 0;
|
|
||||||
if (!isSecret) {
|
|
||||||
MatrixCursor headerCursor = new MatrixCursor(KeyAdapter.PROJECTION);
|
|
||||||
Long[] row = new Long[KeyAdapter.PROJECTION.length];
|
|
||||||
row[KeyAdapter.INDEX_HAS_ANY_SECRET] = 1L;
|
|
||||||
row[KeyAdapter.INDEX_MASTER_KEY_ID] = 0L;
|
|
||||||
headerCursor.addRow(row);
|
|
||||||
|
|
||||||
Cursor dataCursor = data;
|
|
||||||
data = new MergeCursor(new Cursor[] {
|
|
||||||
headerCursor, dataCursor
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getAdapter().swapCursor(data);
|
|
||||||
|
|
||||||
// end action mode, if any
|
// end action mode, if any
|
||||||
if (mActionMode != null) {
|
if (mActionMode != null) {
|
||||||
@@ -354,44 +367,6 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
getAdapter().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) {
|
|
||||||
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
|
|
||||||
viewIntent.setData(
|
|
||||||
KeyRings.buildGenericKeyRingUri(getAdapter().getMasterKeyId(position)));
|
|
||||||
startActivityForResult(viewIntent, REQUEST_VIEW_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void encrypt(ActionMode mode, long[] masterKeyIds) {
|
|
||||||
Intent intent = new Intent(getActivity(), EncryptFilesActivity.class);
|
|
||||||
intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA);
|
|
||||||
intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, masterKeyIds);
|
|
||||||
// used instead of startActivity set actionbar based on callingPackage
|
|
||||||
startActivityForResult(intent, REQUEST_ACTION);
|
|
||||||
|
|
||||||
mode.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show dialog to delete key
|
|
||||||
*
|
|
||||||
* @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not
|
|
||||||
*/
|
|
||||||
public void showDeleteKeyDialog(long[] masterKeyIds, boolean hasSecret) {
|
|
||||||
Intent intent = new Intent(getActivity(), DeleteKeyDialogActivity.class);
|
|
||||||
intent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS, masterKeyIds);
|
|
||||||
intent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, hasSecret);
|
|
||||||
if (hasSecret) {
|
|
||||||
intent.putExtra(DeleteKeyDialogActivity.EXTRA_KEYSERVER,
|
|
||||||
Preferences.getPreferences(getActivity()).getPreferredKeyserver());
|
|
||||||
}
|
|
||||||
startActivityForResult(intent, REQUEST_DELETE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.key_list, menu);
|
inflater.inflate(R.menu.key_list, menu);
|
||||||
@@ -406,7 +381,6 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
|
|
||||||
// Get the searchview
|
// Get the searchview
|
||||||
MenuItem searchItem = menu.findItem(R.id.menu_key_list_search);
|
MenuItem searchItem = menu.findItem(R.id.menu_key_list_search);
|
||||||
|
|
||||||
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||||
|
|
||||||
// Execute this when searching
|
// Execute this when searching
|
||||||
@@ -551,7 +525,6 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProviderHelper providerHelper = new ProviderHelper(activity);
|
ProviderHelper providerHelper = new ProviderHelper(activity);
|
||||||
|
|
||||||
Cursor cursor = providerHelper.getContentResolver().query(
|
Cursor cursor = providerHelper.getContentResolver().query(
|
||||||
KeyRings.buildUnifiedKeyRingsUri(), new String[]{
|
KeyRings.buildUnifiedKeyRingsUri(), new String[]{
|
||||||
KeyRings.FINGERPRINT
|
KeyRings.FINGERPRINT
|
||||||
@@ -566,7 +539,7 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
|
ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
byte[] blob = cursor.getBlob(0);//fingerprint column is 0
|
byte[] blob = cursor.getBlob(0); //fingerprint column is 0
|
||||||
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
|
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
|
||||||
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null);
|
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null);
|
||||||
keyList.add(keyEntry);
|
keyList.add(keyEntry);
|
||||||
@@ -585,7 +558,6 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void consolidate() {
|
private void consolidate() {
|
||||||
|
|
||||||
CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> callback
|
CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> callback
|
||||||
= new CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult>() {
|
= new CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult>() {
|
||||||
|
|
||||||
@@ -615,14 +587,11 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mConsolidateOpHelper =
|
mConsolidateOpHelper = new CryptoOperationHelper<>(2, this, callback, R.string.progress_importing);
|
||||||
new CryptoOperationHelper<>(2, this, callback, R.string.progress_importing);
|
|
||||||
|
|
||||||
mConsolidateOpHelper.cryptoOperation();
|
mConsolidateOpHelper.cryptoOperation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void benchmark() {
|
private void benchmark() {
|
||||||
|
|
||||||
CryptoOperationHelper.Callback<BenchmarkInputParcel, BenchmarkResult> callback
|
CryptoOperationHelper.Callback<BenchmarkInputParcel, BenchmarkResult> callback
|
||||||
= new CryptoOperationHelper.Callback<BenchmarkInputParcel, BenchmarkResult>() {
|
= new CryptoOperationHelper.Callback<BenchmarkInputParcel, BenchmarkResult>() {
|
||||||
|
|
||||||
@@ -652,9 +621,7 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CryptoOperationHelper opHelper =
|
CryptoOperationHelper opHelper = new CryptoOperationHelper<>(2, this, callback, R.string.progress_importing);
|
||||||
new CryptoOperationHelper<>(2, this, callback, R.string.progress_importing);
|
|
||||||
|
|
||||||
opHelper.cryptoOperation();
|
opHelper.cryptoOperation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,6 +640,7 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
if (mActionMode != null) {
|
if (mActionMode != null) {
|
||||||
mActionMode.finish();
|
mActionMode.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
|
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
|
||||||
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
|
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
|
||||||
result.createNotify(getActivity()).show();
|
result.createNotify(getActivity()).show();
|
||||||
@@ -744,199 +712,4 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter {
|
|
||||||
|
|
||||||
private HashMap<Integer, Boolean> mSelection = new HashMap<>();
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
public KeyListAdapter(Context context, Cursor c, int flags) {
|
|
||||||
super(context, c, flags);
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
|
||||||
View view = super.newView(context, cursor, parent);
|
|
||||||
|
|
||||||
final KeyItemViewHolder holder = (KeyItemViewHolder) view.getTag();
|
|
||||||
|
|
||||||
holder.mSlinger.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
ContentDescriptionHint.setup(holder.mSlingerButton);
|
|
||||||
holder.mSlingerButton.setOnClickListener(new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (holder.mMasterKeyId != null) {
|
|
||||||
Intent safeSlingerIntent = new Intent(mContext, SafeSlingerActivity.class);
|
|
||||||
safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, holder.mMasterKeyId);
|
|
||||||
startActivityForResult(safeSlingerIntent, REQUEST_ACTION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
// let the adapter handle setting up the row views
|
|
||||||
View v = super.getView(position, convertView, parent);
|
|
||||||
|
|
||||||
int colorEmphasis = FormattingUtils.getColorFromAttr(mContext, R.attr.colorEmphasis);
|
|
||||||
|
|
||||||
if (mSelection.get(position) != null) {
|
|
||||||
// selected position color
|
|
||||||
v.setBackgroundColor(colorEmphasis);
|
|
||||||
} else {
|
|
||||||
// default color
|
|
||||||
v.setBackgroundColor(Color.TRANSPARENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
boolean isSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0;
|
|
||||||
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
|
|
||||||
if (isSecret && masterKeyId == 0L) {
|
|
||||||
|
|
||||||
// sort of a hack: if this item isn't enabled, we make it clickable
|
|
||||||
// to intercept its click events
|
|
||||||
view.setClickable(true);
|
|
||||||
|
|
||||||
KeyItemViewHolder h = (KeyItemViewHolder) view.getTag();
|
|
||||||
h.setDummy(new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
createKey();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.bindView(view, context, cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HeaderViewHolder {
|
|
||||||
TextView mText;
|
|
||||||
TextView mCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getHeaderView(int position, View convertView, ViewGroup parent) {
|
|
||||||
HeaderViewHolder holder;
|
|
||||||
if (convertView == null) {
|
|
||||||
holder = new HeaderViewHolder();
|
|
||||||
convertView = mInflater.inflate(R.layout.key_list_header_public, parent, false);
|
|
||||||
holder.mText = (TextView) convertView.findViewById(R.id.stickylist_header_text);
|
|
||||||
holder.mCount = (TextView) convertView.findViewById(R.id.contacts_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0) {
|
|
||||||
{ // set contact count
|
|
||||||
int num = mCursor.getCount();
|
|
||||||
// If this is a dummy secret key, subtract one
|
|
||||||
if (mCursor.getLong(INDEX_MASTER_KEY_ID) == 0L) {
|
|
||||||
num -= 1;
|
|
||||||
}
|
|
||||||
String contactsTotal = mContext.getResources().getQuantityString(R.plurals.n_keys, num, num);
|
|
||||||
holder.mCount.setText(contactsTotal);
|
|
||||||
holder.mCount.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.mText.setText(convertView.getResources().getString(R.string.my_keys));
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set header text as first char in user id
|
|
||||||
String userId = mCursor.getString(INDEX_USER_ID);
|
|
||||||
String headerText = convertView.getResources().getString(R.string.user_id_no_name);
|
|
||||||
if (userId != null && userId.length() > 0) {
|
|
||||||
headerText = "" + userId.charAt(0);
|
|
||||||
}
|
|
||||||
holder.mText.setText(headerText);
|
|
||||||
holder.mCount.setVisibility(View.GONE);
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// early breakout: all secret keys are assigned id 0
|
|
||||||
if (mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0) {
|
|
||||||
return 1L;
|
|
||||||
}
|
|
||||||
// otherwise, return the first character of the name as ID
|
|
||||||
String userId = mCursor.getString(INDEX_USER_ID);
|
|
||||||
if (userId != null && userId.length() > 0) {
|
|
||||||
return Character.toUpperCase(userId.charAt(0));
|
|
||||||
} else {
|
|
||||||
return Long.MAX_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNewSelection(int position, boolean value) {
|
|
||||||
mSelection.put(position, value);
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAnySecretSelected() {
|
|
||||||
for (int pos : mSelection.keySet()) {
|
|
||||||
if (isSecretAvailable(pos)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getCurrentSelectedMasterKeyIds() {
|
|
||||||
long[] ids = new long[mSelection.size()];
|
|
||||||
int i = 0;
|
|
||||||
// get master key ids
|
|
||||||
for (int pos : mSelection.keySet()) {
|
|
||||||
ids[i++] = getMasterKeyId(pos);
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeSelection(int position) {
|
|
||||||
mSelection.remove(position);
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearSelection() {
|
|
||||||
mSelection.clear();
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ package org.sufficientlysecure.keychain.ui.adapter;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
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.graphics.PorterDuff;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
import android.util.SparseBooleanArray;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -17,13 +20,21 @@ import android.widget.TextView;
|
|||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.adapter.*;
|
import org.sufficientlysecure.keychain.ui.util.adapter.*;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
public class KeySectionedListAdapter extends SectionCursorAdapter<Character, SectionCursorAdapter.ViewHolder, KeySectionedListAdapter.KeyHeaderViewHolder> implements org.sufficientlysecure.keychain.ui.util.adapter.KeyAdapter {
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KeySectionedListAdapter extends SectionCursorAdapter<KeySectionedListAdapter.KeyCursor, Character,
|
||||||
|
SectionCursorAdapter.ViewHolder, KeySectionedListAdapter.KeyHeaderViewHolder> {
|
||||||
|
|
||||||
private static final short VIEW_ITEM_TYPE_KEY = 0x0;
|
private static final short VIEW_ITEM_TYPE_KEY = 0x0;
|
||||||
private static final short VIEW_ITEM_TYPE_DUMMY = 0x1;
|
private static final short VIEW_ITEM_TYPE_DUMMY = 0x1;
|
||||||
|
|
||||||
@@ -31,57 +42,109 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<Character, Sec
|
|||||||
private static final short VIEW_SECTION_TYPE_PUBLIC = 0x1;
|
private static final short VIEW_SECTION_TYPE_PUBLIC = 0x1;
|
||||||
|
|
||||||
private String mQuery;
|
private String mQuery;
|
||||||
private SparseBooleanArray mSelectionMap;
|
private List<Integer> mSelected;
|
||||||
|
private KeyListListener mListener;
|
||||||
|
|
||||||
private boolean mHasDummy = false;
|
private boolean mHasDummy = false;
|
||||||
|
|
||||||
public KeySectionedListAdapter(Context context, Cursor cursor) {
|
public KeySectionedListAdapter(Context context, Cursor cursor) {
|
||||||
super(context, cursor, 0);
|
super(context, KeyCursor.wrap(cursor), 0);
|
||||||
|
|
||||||
mQuery = "";
|
mQuery = "";
|
||||||
mSelectionMap = new SparseBooleanArray();
|
mSelected = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSearchQuery(String query) {
|
public void setSearchQuery(String query) {
|
||||||
mQuery = query;
|
mQuery = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(Cursor cursor) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeyItem getItem(int position) {
|
|
||||||
Cursor cursor = getCursor();
|
|
||||||
|
|
||||||
if(cursor != null) {
|
|
||||||
if(cursor.getPosition() != position) {
|
|
||||||
moveCursor(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new KeyItem(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMasterKeyId(int position) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSecretAvailable(int position) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContentChanged() {
|
public void onContentChanged() {
|
||||||
mHasDummy = false;
|
mHasDummy = false;
|
||||||
|
mSelected.clear();
|
||||||
|
|
||||||
|
if(mListener != null) {
|
||||||
|
mListener.onSelectionStateChanged(0);
|
||||||
|
}
|
||||||
|
|
||||||
super.onContentChanged();
|
super.onContentChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyCursor swapCursor(KeyCursor cursor) {
|
||||||
|
if (cursor != null && (mQuery == null || TextUtils.isEmpty(mQuery))) {
|
||||||
|
boolean isSecret = cursor.moveToFirst() && cursor.isSecret();
|
||||||
|
|
||||||
|
if (!isSecret) {
|
||||||
|
MatrixCursor headerCursor = new MatrixCursor(KeyCursor.PROJECTION);
|
||||||
|
Long[] row = new Long[KeyCursor.PROJECTION.length];
|
||||||
|
row[KeyCursor.INDEX_HAS_ANY_SECRET] = 1L;
|
||||||
|
row[KeyCursor.INDEX_MASTER_KEY_ID] = 0L;
|
||||||
|
headerCursor.addRow(row);
|
||||||
|
|
||||||
|
Cursor[] toMerge = {
|
||||||
|
headerCursor,
|
||||||
|
cursor.getWrappedCursor()
|
||||||
|
};
|
||||||
|
|
||||||
|
cursor = KeyCursor.wrap(new MergeCursor(toMerge));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (KeyCursor) super.swapCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyListener(KeyListListener listener) {
|
||||||
|
mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSelectedCount() {
|
||||||
|
return mSelected.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectPosition(int position) {
|
||||||
|
mSelected.add(position);
|
||||||
|
notifyItemChanged(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deselectPosition(int position) {
|
||||||
|
mSelected.remove(Integer.valueOf(position));
|
||||||
|
notifyItemChanged(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSelected(int position) {
|
||||||
|
return mSelected.contains(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] getSelectedMasterKeyIds() {
|
||||||
|
long[] keys = new long[mSelected.size()];
|
||||||
|
for(int i = 0; i < keys.length; i++) {
|
||||||
|
if(!moveCursor(mSelected.get(i))) {
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
keys[i] = getIdFromCursor(getCursor());
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAnySecretKeySelected() {
|
||||||
|
for(int i = 0; i < mSelected.size(); i++) {
|
||||||
|
if(!moveCursor(mSelected.get(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getCursor().isSecret()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of database entries displayed.
|
* Returns the number of database entries displayed.
|
||||||
* @return The item count
|
* @return The item count
|
||||||
@@ -95,15 +158,20 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<Character, Sec
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Character getSectionFromCursor(Cursor cursor) throws IllegalStateException {
|
public long getIdFromCursor(KeyCursor cursor) {
|
||||||
if (cursor.getInt(INDEX_HAS_ANY_SECRET) != 0) {
|
return cursor.getKeyId();
|
||||||
if (cursor.getLong(INDEX_MASTER_KEY_ID) == 0L) {
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Character getSectionFromCursor(KeyCursor cursor) throws IllegalStateException {
|
||||||
|
if (cursor.isSecret()) {
|
||||||
|
if (cursor.getKeyId() == 0L) {
|
||||||
mHasDummy = true;
|
mHasDummy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '#';
|
return '#';
|
||||||
} else {
|
} else {
|
||||||
String userId = cursor.getString(INDEX_USER_ID);
|
String userId = cursor.getRawUserId();
|
||||||
if(TextUtils.isEmpty(userId)) {
|
if(TextUtils.isEmpty(userId)) {
|
||||||
return '?';
|
return '?';
|
||||||
} else {
|
} else {
|
||||||
@@ -122,10 +190,9 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<Character, Sec
|
|||||||
@Override
|
@Override
|
||||||
protected short getSectionItemViewType(int position) {
|
protected short getSectionItemViewType(int position) {
|
||||||
if(moveCursor(position)) {
|
if(moveCursor(position)) {
|
||||||
boolean hasMaster = getCursor().getLong(INDEX_MASTER_KEY_ID) != 0L;
|
KeyCursor c = getCursor();
|
||||||
boolean isSecret = getCursor().getInt(INDEX_HAS_ANY_SECRET) != 0;
|
|
||||||
|
|
||||||
if (isSecret && !hasMaster) {
|
if (c.isSecret() && c.getKeyId() == 0L) {
|
||||||
return VIEW_ITEM_TYPE_DUMMY;
|
return VIEW_ITEM_TYPE_DUMMY;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -193,34 +260,47 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<Character, Sec
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onBindItemViewHolder(ViewHolder holder, Cursor cursor) {
|
protected void onBindItemViewHolder(ViewHolder holder, KeyCursor cursor) {
|
||||||
if (holder.getItemViewTypeWithoutSections() == VIEW_ITEM_TYPE_KEY) {
|
if (holder.getItemViewTypeWithoutSections() == VIEW_ITEM_TYPE_KEY) {
|
||||||
Highlighter highlighter = new Highlighter(getContext(), mQuery);
|
Highlighter highlighter = new Highlighter(getContext(), mQuery);
|
||||||
((KeyItemViewHolder) holder).bindKey(new KeyItem(cursor), highlighter);
|
((KeyItemViewHolder) holder).bindKey(cursor, highlighter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class KeyDummyViewHolder extends SectionCursorAdapter.ViewHolder
|
public void finishSelection() {
|
||||||
|
Integer[] selected = mSelected.toArray(
|
||||||
|
new Integer[mSelected.size()]
|
||||||
|
);
|
||||||
|
|
||||||
|
mSelected.clear();
|
||||||
|
|
||||||
|
for(int i = 0; i < selected.length; i++) {
|
||||||
|
notifyItemChanged(selected[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KeyDummyViewHolder extends SectionCursorAdapter.ViewHolder
|
||||||
implements View.OnClickListener{
|
implements View.OnClickListener{
|
||||||
|
|
||||||
public KeyDummyViewHolder(View itemView) {
|
KeyDummyViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
|
||||||
itemView.setClickable(true);
|
itemView.setClickable(true);
|
||||||
itemView.setOnClickListener(this);
|
itemView.setOnClickListener(this);
|
||||||
|
itemView.setEnabled(getSelectedCount() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
|
if(mListener != null) {
|
||||||
|
mListener.onKeyDummyItemClicked();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class KeyItemViewHolder extends SectionCursorAdapter.ViewHolder
|
private class KeyItemViewHolder extends SectionCursorAdapter.ViewHolder
|
||||||
implements View.OnClickListener, View.OnLongClickListener {
|
implements View.OnClickListener, View.OnLongClickListener {
|
||||||
|
|
||||||
private View mLayoutData;
|
|
||||||
private Long mMasterKeyId;
|
|
||||||
private TextView mMainUserId;
|
private TextView mMainUserId;
|
||||||
private TextView mMainUserIdRest;
|
private TextView mMainUserIdRest;
|
||||||
private TextView mCreationDate;
|
private TextView mCreationDate;
|
||||||
@@ -228,13 +308,9 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<Character, Sec
|
|||||||
private View mSlinger;
|
private View mSlinger;
|
||||||
private ImageButton mSlingerButton;
|
private ImageButton mSlingerButton;
|
||||||
|
|
||||||
public KeyItemViewHolder(View itemView) {
|
KeyItemViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
|
||||||
itemView.setOnClickListener(this);
|
|
||||||
itemView.setOnLongClickListener(this);
|
|
||||||
|
|
||||||
mLayoutData = itemView.findViewById(R.id.key_list_item_data);
|
|
||||||
mMainUserId = (TextView) itemView.findViewById(R.id.key_list_item_name);
|
mMainUserId = (TextView) itemView.findViewById(R.id.key_list_item_name);
|
||||||
mMainUserIdRest = (TextView) itemView.findViewById(R.id.key_list_item_email);
|
mMainUserIdRest = (TextView) itemView.findViewById(R.id.key_list_item_email);
|
||||||
mStatus = (ImageView) itemView.findViewById(R.id.key_list_item_status_icon);
|
mStatus = (ImageView) itemView.findViewById(R.id.key_list_item_status_icon);
|
||||||
@@ -242,14 +318,21 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<Character, Sec
|
|||||||
mSlingerButton = (ImageButton) itemView.findViewById(R.id.key_list_item_slinger_button);
|
mSlingerButton = (ImageButton) itemView.findViewById(R.id.key_list_item_slinger_button);
|
||||||
mCreationDate = (TextView) itemView.findViewById(R.id.key_list_item_creation);
|
mCreationDate = (TextView) itemView.findViewById(R.id.key_list_item_creation);
|
||||||
|
|
||||||
|
itemView.setClickable(true);
|
||||||
|
itemView.setLongClickable(true);
|
||||||
|
itemView.setOnClickListener(this);
|
||||||
|
itemView.setOnLongClickListener(this);
|
||||||
|
|
||||||
|
mSlingerButton.setClickable(true);
|
||||||
mSlingerButton.setOnClickListener(this);
|
mSlingerButton.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindKey(KeyItem keyItem, Highlighter highlighter) {
|
void bindKey(KeyCursor keyItem, Highlighter highlighter) {
|
||||||
Context ctx = itemView.getContext();
|
itemView.setSelected(isSelected(getAdapterPosition()));
|
||||||
|
Context context = itemView.getContext();
|
||||||
|
|
||||||
{ // set name and stuff, common to both key types
|
{ // set name and stuff, common to both key types
|
||||||
OpenPgpUtils.UserId userIdSplit = keyItem.mUserId;
|
OpenPgpUtils.UserId userIdSplit = keyItem.getUserId();
|
||||||
if (userIdSplit.name != null) {
|
if (userIdSplit.name != null) {
|
||||||
mMainUserId.setText(highlighter.highlight(userIdSplit.name));
|
mMainUserId.setText(highlighter.highlight(userIdSplit.name));
|
||||||
} else {
|
} else {
|
||||||
@@ -263,63 +346,79 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<Character, Sec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort of a hack: if this item isn't enabled, we make it clickable
|
{ // set edit button and status, specific by key type. Note: order is important!
|
||||||
// to intercept its click events. either way, no listener!
|
|
||||||
itemView.setClickable(false);
|
|
||||||
|
|
||||||
{ // set edit button and status, specific by key type
|
|
||||||
|
|
||||||
mMasterKeyId = keyItem.mKeyId;
|
|
||||||
|
|
||||||
int textColor;
|
int textColor;
|
||||||
|
if (keyItem.isRevoked()) {
|
||||||
|
KeyFormattingUtils.setStatusImage(
|
||||||
|
context,
|
||||||
|
mStatus,
|
||||||
|
null,
|
||||||
|
KeyFormattingUtils.State.REVOKED,
|
||||||
|
R.color.key_flag_gray
|
||||||
|
);
|
||||||
|
|
||||||
// Note: order is important!
|
|
||||||
if (keyItem.mIsRevoked) {
|
|
||||||
KeyFormattingUtils
|
|
||||||
.setStatusImage(ctx, mStatus, null, KeyFormattingUtils.State.REVOKED, R.color.key_flag_gray);
|
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
mStatus.setVisibility(View.VISIBLE);
|
||||||
mSlinger.setVisibility(View.GONE);
|
mSlinger.setVisibility(View.GONE);
|
||||||
textColor = ContextCompat.getColor(ctx, R.color.key_flag_gray);
|
textColor = ContextCompat.getColor(context, R.color.key_flag_gray);
|
||||||
} else if (keyItem.mIsExpired) {
|
} else if (keyItem.isExpired()) {
|
||||||
KeyFormattingUtils.setStatusImage(ctx, mStatus, null, KeyFormattingUtils.State.EXPIRED, R.color.key_flag_gray);
|
KeyFormattingUtils.setStatusImage(
|
||||||
|
context,
|
||||||
|
mStatus,
|
||||||
|
null,
|
||||||
|
KeyFormattingUtils.State.EXPIRED,
|
||||||
|
R.color.key_flag_gray
|
||||||
|
);
|
||||||
|
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
mStatus.setVisibility(View.VISIBLE);
|
||||||
mSlinger.setVisibility(View.GONE);
|
mSlinger.setVisibility(View.GONE);
|
||||||
textColor = ContextCompat.getColor(ctx, R.color.key_flag_gray);
|
textColor = ContextCompat.getColor(context, R.color.key_flag_gray);
|
||||||
} else if (keyItem.mIsSecret) {
|
} else if (keyItem.isSecret()) {
|
||||||
mStatus.setVisibility(View.GONE);
|
mStatus.setVisibility(View.GONE);
|
||||||
if (mSlingerButton.hasOnClickListeners()) {
|
if (mSlingerButton.hasOnClickListeners()) {
|
||||||
mSlingerButton.setColorFilter(
|
mSlingerButton.setColorFilter(
|
||||||
FormattingUtils.getColorFromAttr(ctx, R.attr.colorTertiaryText),
|
FormattingUtils.getColorFromAttr(context, R.attr.colorTertiaryText),
|
||||||
PorterDuff.Mode.SRC_IN);
|
PorterDuff.Mode.SRC_IN
|
||||||
|
);
|
||||||
|
|
||||||
mSlinger.setVisibility(View.VISIBLE);
|
mSlinger.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
mSlinger.setVisibility(View.GONE);
|
mSlinger.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
textColor = FormattingUtils.getColorFromAttr(ctx, R.attr.colorText);
|
textColor = FormattingUtils.getColorFromAttr(context, R.attr.colorText);
|
||||||
} else {
|
} else {
|
||||||
// this is a public key - show if it's verified
|
// this is a public key - show if it's verified
|
||||||
if (keyItem.mIsVerified) {
|
if (keyItem.isVerified()) {
|
||||||
KeyFormattingUtils.setStatusImage(ctx, mStatus, KeyFormattingUtils.State.VERIFIED);
|
KeyFormattingUtils.setStatusImage(
|
||||||
|
context,
|
||||||
|
mStatus,
|
||||||
|
KeyFormattingUtils.State.VERIFIED
|
||||||
|
);
|
||||||
|
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
mStatus.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
KeyFormattingUtils.setStatusImage(ctx, mStatus, KeyFormattingUtils.State.UNVERIFIED);
|
KeyFormattingUtils.setStatusImage(
|
||||||
|
context,
|
||||||
|
mStatus,
|
||||||
|
KeyFormattingUtils.State.UNVERIFIED
|
||||||
|
);
|
||||||
|
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
mStatus.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
mSlinger.setVisibility(View.GONE);
|
mSlinger.setVisibility(View.GONE);
|
||||||
textColor = FormattingUtils.getColorFromAttr(ctx, R.attr.colorText);
|
textColor = FormattingUtils.getColorFromAttr(context, R.attr.colorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
mMainUserId.setTextColor(textColor);
|
mMainUserId.setTextColor(textColor);
|
||||||
mMainUserIdRest.setTextColor(textColor);
|
mMainUserIdRest.setTextColor(textColor);
|
||||||
|
|
||||||
if (keyItem.mHasDuplicate) {
|
if (keyItem.hasDuplicate()) {
|
||||||
String dateTime = DateUtils.formatDateTime(ctx,
|
String dateTime = DateUtils.formatDateTime(context,
|
||||||
keyItem.mCreation.getTime(),
|
keyItem.getCreationTime(),
|
||||||
DateUtils.FORMAT_SHOW_DATE
|
DateUtils.FORMAT_SHOW_DATE
|
||||||
| DateUtils.FORMAT_SHOW_TIME
|
| DateUtils.FORMAT_SHOW_TIME
|
||||||
| DateUtils.FORMAT_SHOW_YEAR
|
| DateUtils.FORMAT_SHOW_YEAR
|
||||||
| DateUtils.FORMAT_ABBREV_MONTH);
|
| DateUtils.FORMAT_ABBREV_MONTH);
|
||||||
mCreationDate.setText(ctx.getString(R.string.label_key_created,
|
mCreationDate.setText(context.getString(R.string.label_key_created,
|
||||||
dateTime));
|
dateTime));
|
||||||
mCreationDate.setTextColor(textColor);
|
mCreationDate.setTextColor(textColor);
|
||||||
mCreationDate.setVisibility(View.VISIBLE);
|
mCreationDate.setVisibility(View.VISIBLE);
|
||||||
@@ -332,11 +431,47 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<Character, Sec
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
int pos = getAdapterPosition();
|
||||||
|
switch (v.getId()) {
|
||||||
|
case R.id.key_list_item_slinger_button:
|
||||||
|
if(mListener != null) {
|
||||||
|
mListener.onSlingerButtonClicked(getItemId());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(getSelectedCount() == 0) {
|
||||||
|
if(mListener != null) {
|
||||||
|
mListener.onKeyItemClicked(getItemId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(isSelected(pos)) {
|
||||||
|
deselectPosition(pos);
|
||||||
|
} else {
|
||||||
|
selectPosition(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mListener != null) {
|
||||||
|
mListener.onSelectionStateChanged(getSelectedCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View v) {
|
public boolean onLongClick(View v) {
|
||||||
|
System.out.println("Long Click!");
|
||||||
|
if(getSelectedCount() == 0) {
|
||||||
|
selectPosition(getAdapterPosition());
|
||||||
|
|
||||||
|
if(mListener != null) {
|
||||||
|
mListener.onSelectionStateChanged(getSelectedCount());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,4 +488,112 @@ public class KeySectionedListAdapter extends SectionCursorAdapter<Character, Sec
|
|||||||
mText1.setText(title);
|
mText1.setText(title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class KeyCursor extends CursorWrapper {
|
||||||
|
public static final String[] PROJECTION = new String[]{
|
||||||
|
KeychainContract.KeyRings._ID,
|
||||||
|
KeychainContract.KeyRings.MASTER_KEY_ID,
|
||||||
|
KeychainContract.KeyRings.USER_ID,
|
||||||
|
KeychainContract.KeyRings.IS_REVOKED,
|
||||||
|
KeychainContract.KeyRings.IS_EXPIRED,
|
||||||
|
KeychainContract.KeyRings.VERIFIED,
|
||||||
|
KeychainContract.KeyRings.HAS_ANY_SECRET,
|
||||||
|
KeychainContract.KeyRings.HAS_DUPLICATE_USER_ID,
|
||||||
|
KeychainContract.KeyRings.FINGERPRINT,
|
||||||
|
KeychainContract.KeyRings.CREATION,
|
||||||
|
KeychainContract.KeyRings.HAS_ENCRYPT
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final int INDEX_ENTRY_ID = 0;
|
||||||
|
public static final int INDEX_MASTER_KEY_ID = 1;
|
||||||
|
public static final int INDEX_USER_ID = 2;
|
||||||
|
public static final int INDEX_IS_REVOKED = 3;
|
||||||
|
public static final int INDEX_IS_EXPIRED = 4;
|
||||||
|
public static final int INDEX_VERIFIED = 5;
|
||||||
|
public static final int INDEX_HAS_ANY_SECRET = 6;
|
||||||
|
public static final int INDEX_HAS_DUPLICATE_USER_ID = 7;
|
||||||
|
public static final int INDEX_FINGERPRINT = 8;
|
||||||
|
public static final int INDEX_CREATION = 9;
|
||||||
|
public static final int INDEX_HAS_ENCRYPT = 10;
|
||||||
|
|
||||||
|
public static KeyCursor wrap(Cursor cursor) {
|
||||||
|
if(cursor != null) {
|
||||||
|
return new KeyCursor(cursor);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a cursor wrapper.
|
||||||
|
*
|
||||||
|
* @param cursor The underlying cursor to wrap.
|
||||||
|
*/
|
||||||
|
private KeyCursor(Cursor cursor) {
|
||||||
|
super(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEntryId() {
|
||||||
|
return getInt(INDEX_ENTRY_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRawUserId() {
|
||||||
|
return getString(INDEX_USER_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenPgpUtils.UserId getUserId() {
|
||||||
|
return KeyRing.splitUserId(getRawUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getKeyId() {
|
||||||
|
return getLong(INDEX_MASTER_KEY_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasDuplicate() {
|
||||||
|
return getLong(INDEX_HAS_DUPLICATE_USER_ID) > 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEncrypt() {
|
||||||
|
return getInt(INDEX_HAS_ENCRYPT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCreationTime() {
|
||||||
|
return getLong(INDEX_CREATION) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreationDate() {
|
||||||
|
return new Date(getCreationTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getRawFingerprint() {
|
||||||
|
return getBlob(INDEX_FINGERPRINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFingerprint() {
|
||||||
|
return KeyFormattingUtils.convertFingerprintToHex(getRawFingerprint());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSecret() {
|
||||||
|
return getInt(INDEX_HAS_ANY_SECRET) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRevoked() {
|
||||||
|
return getInt(INDEX_IS_REVOKED) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExpired() {
|
||||||
|
return getInt(INDEX_IS_EXPIRED) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVerified() {
|
||||||
|
return getInt(INDEX_VERIFIED) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface KeyListListener {
|
||||||
|
void onKeyDummyItemClicked();
|
||||||
|
void onKeyItemClicked(long masterKeyId);
|
||||||
|
void onSlingerButtonClicked(long masterKeyId);
|
||||||
|
void onSelectionStateChanged(int selectedCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import android.support.v7.widget.RecyclerView;
|
|||||||
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
public abstract class CursorAdapter extends RecyclerView.Adapter {
|
public abstract class CursorAdapter<C extends Cursor> extends RecyclerView.Adapter {
|
||||||
|
|
||||||
public static final String TAG = "CursorAdapter";
|
public static final String TAG = "CursorAdapter";
|
||||||
|
|
||||||
private Cursor mCursor;
|
private C mCursor;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private boolean mDataValid;
|
private boolean mDataValid;
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ public abstract class CursorAdapter extends RecyclerView.Adapter {
|
|||||||
* @param c The cursor from which to get the data.
|
* @param c The cursor from which to get the data.
|
||||||
* @param context The context
|
* @param context The context
|
||||||
*/
|
*/
|
||||||
public CursorAdapter(Context context, Cursor c) {
|
public CursorAdapter(Context context, C c) {
|
||||||
init(context, c, FLAG_REGISTER_CONTENT_OBSERVER);
|
init(context, c, FLAG_REGISTER_CONTENT_OBSERVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,11 +51,11 @@ public abstract class CursorAdapter extends RecyclerView.Adapter {
|
|||||||
* @param flags Flags used to determine the behavior of the adapter
|
* @param flags Flags used to determine the behavior of the adapter
|
||||||
* @see #FLAG_REGISTER_CONTENT_OBSERVER
|
* @see #FLAG_REGISTER_CONTENT_OBSERVER
|
||||||
*/
|
*/
|
||||||
public CursorAdapter(Context context, Cursor c, int flags) {
|
public CursorAdapter(Context context, C c, int flags) {
|
||||||
init(context, c, flags);
|
init(context, c, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(Context context, Cursor c, int flags) {
|
private void init(Context context, C c, int flags) {
|
||||||
boolean cursorPresent = c != null;
|
boolean cursorPresent = c != null;
|
||||||
mCursor = c;
|
mCursor = c;
|
||||||
mDataValid = cursorPresent;
|
mDataValid = cursorPresent;
|
||||||
@@ -80,7 +80,7 @@ public abstract class CursorAdapter extends RecyclerView.Adapter {
|
|||||||
* Returns the cursor.
|
* Returns the cursor.
|
||||||
* @return the cursor.
|
* @return the cursor.
|
||||||
*/
|
*/
|
||||||
public Cursor getCursor() {
|
public C getCursor() {
|
||||||
return mCursor;
|
return mCursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ public abstract class CursorAdapter extends RecyclerView.Adapter {
|
|||||||
* @param cursor The cursor moved to the correct position.
|
* @param cursor The cursor moved to the correct position.
|
||||||
* @return The id of the dataset
|
* @return The id of the dataset
|
||||||
*/
|
*/
|
||||||
public long getIdFromCursor(Cursor cursor) {
|
public long getIdFromCursor(C cursor) {
|
||||||
if(cursor != null) {
|
if(cursor != null) {
|
||||||
return cursor.getPosition();
|
return cursor.getPosition();
|
||||||
} else {
|
} else {
|
||||||
@@ -185,7 +185,7 @@ public abstract class CursorAdapter extends RecyclerView.Adapter {
|
|||||||
*
|
*
|
||||||
* @param cursor The new cursor to be used
|
* @param cursor The new cursor to be used
|
||||||
*/
|
*/
|
||||||
public void changeCursor(Cursor cursor) {
|
public void changeCursor(C cursor) {
|
||||||
Cursor old = swapCursor(cursor);
|
Cursor old = swapCursor(cursor);
|
||||||
if (old != null) {
|
if (old != null) {
|
||||||
old.close();
|
old.close();
|
||||||
@@ -202,7 +202,7 @@ public abstract class CursorAdapter extends RecyclerView.Adapter {
|
|||||||
* If the given new Cursor is the same instance is the previously set
|
* If the given new Cursor is the same instance is the previously set
|
||||||
* Cursor, null is also returned.
|
* Cursor, null is also returned.
|
||||||
*/
|
*/
|
||||||
public Cursor swapCursor(Cursor newCursor) {
|
public Cursor swapCursor(C newCursor) {
|
||||||
if (newCursor == mCursor) {
|
if (newCursor == mCursor) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
package org.sufficientlysecure.keychain.ui.util.adapter;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public interface KeyAdapter {
|
|
||||||
// These are the rows that we will retrieve.
|
|
||||||
String[] PROJECTION = new String[] {
|
|
||||||
KeychainContract.KeyRings._ID,
|
|
||||||
KeychainContract.KeyRings.MASTER_KEY_ID,
|
|
||||||
KeychainContract.KeyRings.USER_ID,
|
|
||||||
KeychainContract.KeyRings.IS_REVOKED,
|
|
||||||
KeychainContract.KeyRings.IS_EXPIRED,
|
|
||||||
KeychainContract.KeyRings.VERIFIED,
|
|
||||||
KeychainContract.KeyRings.HAS_ANY_SECRET,
|
|
||||||
KeychainContract.KeyRings.HAS_DUPLICATE_USER_ID,
|
|
||||||
KeychainContract.KeyRings.FINGERPRINT,
|
|
||||||
KeychainContract.KeyRings.CREATION,
|
|
||||||
KeychainContract.KeyRings.HAS_ENCRYPT
|
|
||||||
};
|
|
||||||
|
|
||||||
// projection indices
|
|
||||||
int INDEX_MASTER_KEY_ID = 1;
|
|
||||||
int INDEX_USER_ID = 2;
|
|
||||||
int INDEX_IS_REVOKED = 3;
|
|
||||||
int INDEX_IS_EXPIRED = 4;
|
|
||||||
int INDEX_VERIFIED = 5;
|
|
||||||
int INDEX_HAS_ANY_SECRET = 6;
|
|
||||||
int INDEX_HAS_DUPLICATE_USER_ID = 7;
|
|
||||||
int INDEX_FINGERPRINT = 8;
|
|
||||||
int INDEX_CREATION = 9;
|
|
||||||
int INDEX_HAS_ENCRYPT = 10;
|
|
||||||
|
|
||||||
// adapter functionality
|
|
||||||
void setSearchQuery(String query);
|
|
||||||
boolean isEnabled(Cursor cursor);
|
|
||||||
|
|
||||||
KeyItem getItem(int position);
|
|
||||||
long getMasterKeyId(int position);
|
|
||||||
boolean isSecretAvailable(int position);
|
|
||||||
|
|
||||||
class KeyItem implements Serializable {
|
|
||||||
public final String mUserIdFull;
|
|
||||||
public final OpenPgpUtils.UserId mUserId;
|
|
||||||
public final long mKeyId;
|
|
||||||
public final boolean mHasDuplicate;
|
|
||||||
public final boolean mHasEncrypt;
|
|
||||||
public final Date mCreation;
|
|
||||||
public final String mFingerprint;
|
|
||||||
public final boolean mIsSecret, mIsRevoked, mIsExpired, mIsVerified;
|
|
||||||
|
|
||||||
public KeyItem(Cursor cursor) {
|
|
||||||
String userId = cursor.getString(INDEX_USER_ID);
|
|
||||||
mUserId = KeyRing.splitUserId(userId);
|
|
||||||
mUserIdFull = userId;
|
|
||||||
mKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
|
|
||||||
mHasDuplicate = cursor.getLong(INDEX_HAS_DUPLICATE_USER_ID) > 0;
|
|
||||||
mHasEncrypt = cursor.getInt(INDEX_HAS_ENCRYPT) != 0;
|
|
||||||
mCreation = new Date(cursor.getLong(INDEX_CREATION) * 1000);
|
|
||||||
mFingerprint = KeyFormattingUtils.convertFingerprintToHex(
|
|
||||||
cursor.getBlob(INDEX_FINGERPRINT));
|
|
||||||
mIsSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0;
|
|
||||||
mIsRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
|
|
||||||
mIsExpired = cursor.getInt(INDEX_IS_EXPIRED) > 0;
|
|
||||||
mIsVerified = cursor.getInt(INDEX_VERIFIED) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyItem(CanonicalizedPublicKeyRing ring) {
|
|
||||||
CanonicalizedPublicKey key = ring.getPublicKey();
|
|
||||||
String userId = key.getPrimaryUserIdWithFallback();
|
|
||||||
mUserId = KeyRing.splitUserId(userId);
|
|
||||||
mUserIdFull = userId;
|
|
||||||
mKeyId = ring.getMasterKeyId();
|
|
||||||
mHasDuplicate = false;
|
|
||||||
mHasEncrypt = key.getKeyRing().getEncryptIds().size() > 0;
|
|
||||||
mCreation = key.getCreationTime();
|
|
||||||
mFingerprint = KeyFormattingUtils.convertFingerprintToHex(
|
|
||||||
ring.getFingerprint());
|
|
||||||
mIsRevoked = key.isRevoked();
|
|
||||||
mIsExpired = key.isExpired();
|
|
||||||
|
|
||||||
// these two are actually "don't know"s
|
|
||||||
mIsSecret = false;
|
|
||||||
mIsVerified = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getReadableName() {
|
|
||||||
if (mUserId.name != null) {
|
|
||||||
return mUserId.name;
|
|
||||||
} else {
|
|
||||||
return mUserId.email;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,13 +9,14 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import com.tonicartos.superslim.LayoutManager;
|
import com.tonicartos.superslim.LayoutManager;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param <T> section type.
|
* @param <T> section type.
|
||||||
* @param <VH> the view holder extending {@code BaseViewHolder<Cursor>} that is bound to the cursor data.
|
* @param <VH> the view holder extending {@code BaseViewHolder<Cursor>} that is bound to the cursor data.
|
||||||
* @param <SH> the view holder extending {@code BaseViewHolder<<T>>} that is bound to the section data.
|
* @param <SH> the view holder extending {@code BaseViewHolder<<T>>} that is bound to the section data.
|
||||||
*/
|
*/
|
||||||
public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.ViewHolder,
|
public abstract class SectionCursorAdapter<C extends Cursor, T, VH extends SectionCursorAdapter.ViewHolder,
|
||||||
SH extends SectionCursorAdapter.ViewHolder> extends CursorAdapter {
|
SH extends SectionCursorAdapter.ViewHolder> extends CursorAdapter<C> {
|
||||||
|
|
||||||
public static final String TAG = "SectionCursorAdapter";
|
public static final String TAG = "SectionCursorAdapter";
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.Vi
|
|||||||
private SparseArrayCompat<T> mSectionMap = new SparseArrayCompat<>();
|
private SparseArrayCompat<T> mSectionMap = new SparseArrayCompat<>();
|
||||||
private Comparator<T> mSectionComparator;
|
private Comparator<T> mSectionComparator;
|
||||||
|
|
||||||
public SectionCursorAdapter(Context context, Cursor cursor, int flags) {
|
public SectionCursorAdapter(Context context, C cursor, int flags) {
|
||||||
this(context, cursor, flags, new Comparator<T>() {
|
this(context, cursor, flags, new Comparator<T>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean equal(T obj1, T obj2) {
|
public boolean equal(T obj1, T obj2) {
|
||||||
@@ -35,7 +36,7 @@ public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.Vi
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public SectionCursorAdapter(Context context, Cursor cursor, int flags, Comparator<T> comparator) {
|
public SectionCursorAdapter(Context context, C cursor, int flags, Comparator<T> comparator) {
|
||||||
super(context, cursor, flags);
|
super(context, cursor, flags);
|
||||||
setSectionComparator(comparator);
|
setSectionComparator(comparator);
|
||||||
}
|
}
|
||||||
@@ -82,7 +83,7 @@ public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.Vi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendSections(Cursor cursor) throws IllegalStateException {
|
private void appendSections(C cursor) throws IllegalStateException {
|
||||||
int cursorPosition = 0;
|
int cursorPosition = 0;
|
||||||
while(hasValidData() && cursor.moveToNext()) {
|
while(hasValidData() && cursor.moveToNext()) {
|
||||||
T section = getSectionFromCursor(cursor);
|
T section = getSectionFromCursor(cursor);
|
||||||
@@ -109,7 +110,7 @@ public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.Vi
|
|||||||
* @return the section from the cursor at its current position.
|
* @return the section from the cursor at its current position.
|
||||||
* This object will be passed to newSectionView and bindSectionView.
|
* This object will be passed to newSectionView and bindSectionView.
|
||||||
*/
|
*/
|
||||||
protected abstract T getSectionFromCursor(Cursor cursor) throws IllegalStateException;
|
protected abstract T getSectionFromCursor(C cursor) throws IllegalStateException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
@@ -119,7 +120,7 @@ public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.Vi
|
|||||||
@Override
|
@Override
|
||||||
public final long getItemId(int listPosition) {
|
public final long getItemId(int listPosition) {
|
||||||
if (isSection(listPosition))
|
if (isSection(listPosition))
|
||||||
return listPosition;
|
return -1;
|
||||||
else {
|
else {
|
||||||
int cursorPosition = getCursorPositionWithoutSections(listPosition);
|
int cursorPosition = getCursorPositionWithoutSections(listPosition);
|
||||||
return super.getItemId(cursorPosition);
|
return super.getItemId(cursorPosition);
|
||||||
@@ -156,6 +157,19 @@ public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.Vi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getListPosition(int cursorPosition) {
|
||||||
|
for(int i = 0; i < mSectionMap.size(); i++) {
|
||||||
|
int sectionIndex = mSectionMap.keyAt(i);
|
||||||
|
if (sectionIndex > cursorPosition) {
|
||||||
|
return cursorPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorPosition +=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursorPosition;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the list position of an item in the adapter, returns the
|
* Given the list position of an item in the adapter, returns the
|
||||||
* adapter position of the first item of the section the given item belongs to.
|
* adapter position of the first item of the section the given item belongs to.
|
||||||
@@ -263,7 +277,7 @@ public abstract class SectionCursorAdapter<T, VH extends SectionCursorAdapter.Vi
|
|||||||
protected abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);
|
protected abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);
|
||||||
|
|
||||||
protected abstract void onBindSectionViewHolder(SH holder, T section);
|
protected abstract void onBindSectionViewHolder(SH holder, T section);
|
||||||
protected abstract void onBindItemViewHolder(VH holder, Cursor cursor);
|
protected abstract void onBindItemViewHolder(VH holder, C cursor);
|
||||||
|
|
||||||
public interface Comparator<T> {
|
public interface Comparator<T> {
|
||||||
boolean equal(T obj1, T obj2);
|
boolean equal(T obj1, T obj2);
|
||||||
|
|||||||
12
OpenKeychain/src/main/res/drawable-v21/list_item_ripple.xml
Normal file
12
OpenKeychain/src/main/res/drawable-v21/list_item_ripple.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?android:colorControlHighlight" >
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<color android:color="@android:color/white" />
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<selector>
|
||||||
|
<item android:state_selected="true" android:drawable="@color/pressed_gray"/>
|
||||||
|
</selector>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
5
OpenKeychain/src/main/res/drawable/list_item_ripple.xml
Normal file
5
OpenKeychain/src/main/res/drawable/list_item_ripple.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_selected="true" android:drawable="@color/selected_gray"/>
|
||||||
|
<item android:state_pressed="true" android:drawable="@color/pressed_gray"/>
|
||||||
|
</selector>
|
||||||
@@ -5,11 +5,12 @@
|
|||||||
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
android:minHeight="?attr/listPreferredItemHeight"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:descendantFocusability="blocksDescendants"
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
android:background="@drawable/list_item_ripple"
|
||||||
android:focusable="false">
|
android:focusable="false">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -37,4 +37,7 @@
|
|||||||
<color name="card_view_button">#7bad45</color>
|
<color name="card_view_button">#7bad45</color>
|
||||||
<color name="toolbar_photo_tint">#1E7bad45</color>
|
<color name="toolbar_photo_tint">#1E7bad45</color>
|
||||||
|
|
||||||
|
<color name="pressed_gray">#0c000000</color>
|
||||||
|
<color name="selected_gray">#2c000000</color>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user