Removing changes associated to KeyListFragment, they will get their own PR.
This commit is contained in:
@@ -23,6 +23,7 @@ dependencies {
|
|||||||
compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0'
|
compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0'
|
||||||
compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
|
compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
|
||||||
compile 'com.splitwise:tokenautocomplete:2.0.7@aar'
|
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:html-textview:1.8'
|
||||||
compile 'org.sufficientlysecure:donations:2.4'
|
compile 'org.sufficientlysecure:donations:2.4'
|
||||||
compile 'com.nispok:snackbar:2.11.0'
|
compile 'com.nispok:snackbar:2.11.0'
|
||||||
@@ -101,6 +102,7 @@ dependencyVerification {
|
|||||||
'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13',
|
'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13',
|
||||||
'org.ocpsoft.prettytime:prettytime:ef7098d973ae78b57d1a22dc37d3b8a771bf030301300e24055d676b6cdc5e75',
|
'org.ocpsoft.prettytime:prettytime:ef7098d973ae78b57d1a22dc37d3b8a771bf030301300e24055d676b6cdc5e75',
|
||||||
'com.splitwise:tokenautocomplete:f56239588390f103b270b7c12361d99b06313a5a0410dc7f66e241ac4baf9baa',
|
'com.splitwise:tokenautocomplete:f56239588390f103b270b7c12361d99b06313a5a0410dc7f66e241ac4baf9baa',
|
||||||
|
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
|
||||||
'org.sufficientlysecure:html-textview:206f484fe4178be6c831fe680de558764967e7b56496c4cc7f37f2979a477df6',
|
'org.sufficientlysecure:html-textview:206f484fe4178be6c831fe680de558764967e7b56496c4cc7f37f2979a477df6',
|
||||||
'org.sufficientlysecure:donations:96f8197bab26dfe41900d824f10f8f1914519cd62eedb77bdac5b223eccdf0a6',
|
'org.sufficientlysecure:donations:96f8197bab26dfe41900d824f10f8f1914519cd62eedb77bdac5b223eccdf0a6',
|
||||||
'com.nispok:snackbar:46b5eb9d630d329e13c2ce00ee9fb115ffb66c23c72cff32ee97eedd76824c6f',
|
'com.nispok:snackbar:46b5eb9d630d329e13c2ce00ee9fb115ffb66c23c72cff32ee97eedd76824c6f',
|
||||||
@@ -110,7 +112,6 @@ dependencyVerification {
|
|||||||
'org.apache.james:apache-mime4j-dom:7e6b06ee164a1c21b7e477249ea0b74a18fddce44764e5764085f58dd8c34633',
|
'org.apache.james:apache-mime4j-dom:7e6b06ee164a1c21b7e477249ea0b74a18fddce44764e5764085f58dd8c34633',
|
||||||
'org.thoughtcrime.ssl.pinning:AndroidPinning:afa1d74e699257fa75cb109ff29bac50726ef269c6e306bdeffe8223cee06ef4',
|
'org.thoughtcrime.ssl.pinning:AndroidPinning:afa1d74e699257fa75cb109ff29bac50726ef269c6e306bdeffe8223cee06ef4',
|
||||||
'com.cocosw:bottomsheet:4af6112a7f4cad4e2b70e5fdf1edc39f51275523a0f53011a012837dc103e597',
|
'com.cocosw:bottomsheet:4af6112a7f4cad4e2b70e5fdf1edc39f51275523a0f53011a012837dc103e597',
|
||||||
'com.tonicartos:superslim:ca89b5c674660cc6918a8f8fd385065bffeee27983e0d33c7c2f0ad7b34d2d49',
|
|
||||||
'com.mikepenz:materialdrawer:4169462fdde042e2bb53a7c2b4e2334d569d16b2020781ee05741b50e1a2967d',
|
'com.mikepenz:materialdrawer:4169462fdde042e2bb53a7c2b4e2334d569d16b2020781ee05741b50e1a2967d',
|
||||||
'com.mikepenz:fastadapter:1bfc00216d71dfdfe0d8e7a9d92bb97bfaa1794543930e34b1f79d5d7adbddf6',
|
'com.mikepenz:fastadapter:1bfc00216d71dfdfe0d8e7a9d92bb97bfaa1794543930e34b1f79d5d7adbddf6',
|
||||||
'com.mikepenz:materialize:575195b2fa5b2414fb14a59470ee21d8a8cd8355b651e0cf52e477e3ff1cd96c',
|
'com.mikepenz:materialize:575195b2fa5b2414fb14a59470ee21d8a8cd8355b651e0cf52e477e3ff1cd96c',
|
||||||
@@ -119,18 +120,19 @@ dependencyVerification {
|
|||||||
'com.mikepenz:fontawesome-typeface:033cf3460d8074bd37a1fefc2ff4eac8f2e3db835ec78bf386d46710e4d0827c',
|
'com.mikepenz:fontawesome-typeface:033cf3460d8074bd37a1fefc2ff4eac8f2e3db835ec78bf386d46710e4d0827c',
|
||||||
'com.mikepenz:community-material-typeface:382e8446fc08fe03cb1e0f91ee329ffd514c113ad22f8389b88424ac71ed5fbb',
|
'com.mikepenz:community-material-typeface:382e8446fc08fe03cb1e0f91ee329ffd514c113ad22f8389b88424ac71ed5fbb',
|
||||||
'com.fidesmo:nordpol-android:56f43fe2b1676817bcb4085926de14a08282ef6729c855c198d81aec62b20d65',
|
'com.fidesmo:nordpol-android:56f43fe2b1676817bcb4085926de14a08282ef6729c855c198d81aec62b20d65',
|
||||||
// 'open-keychain:openpgp-api-lib:00d6ae7e3c4cc9b9ffbb279ba511732fb30c2fb3c5f408171c74cce9b1dfe67f',
|
// 'OpenKeychain:openpgp-api-lib:2c145be0d124d37558f65ed962c47358b7e424dd4c00dc8818aad64598664c3d',
|
||||||
// 'open-keychain:openkeychain-api-lib:842dd4c4469e3e1e76790bd37c8474422d385b09a6cb55d0baeacabfdaf0bc58',
|
// 'OpenKeychain:openkeychain-api-lib:afff9f8410d8781fcd4023ca247d876ca124a6ee4f718afbc3740fc6078a255d',
|
||||||
// 'open-keychain.extern.bouncycastle:core:cab27a0fe17ea07a94624c29855ac8bc8f8d55dff277349007152bc778959f03',
|
// 'OpenKeychain.extern.bouncycastle:core:18876ec7629c427002ee00bb361b016b86159dcd9aac68951aec92f9ca41bb7a',
|
||||||
// 'open-keychain.extern.bouncycastle:pg:c45a5caf65c5b0e7be2c4b59948fb3717f93fd58d680191b7b20d3f1898cdb9a',
|
// 'OpenKeychain.extern.bouncycastle:pg:b6469f69d17ecb43c86c082fb1b5c01ae2f8e7a8ca5fa29e9f199c74dc021be1',
|
||||||
// 'open-keychain.extern.bouncycastle:prov:12f98852e8d3f129816516ea7a244a218eb7f7f0423c66e5084f35421e286c8e',
|
// 'OpenKeychain.extern.bouncycastle:prov:db7c1a72a86d918a25fc1ca8553762d1280274e8e2f02299f344b244bb527708',
|
||||||
// 'open-keychain.extern:minidns:f3deaa17cf92e0b1828e6f261cc423ff8a11c4717c25f5a2edf75d53a7a164e0',
|
// 'OpenKeychain.extern:minidns:8b324812d4b9ea9d639be43183f4292e5f257319fbb6c23faf2ee420f041d279',
|
||||||
// 'open-keychain:KeybaseLib:5ebab9bc7aa43271c80f2de91216c886264ab30289ae4fb5cd08fbde9aa10981',
|
// 'OpenKeychain:KeybaseLib:a992fb93c718fa34ab45850e34a294e413c9410e2b00243a43e6f303d85b6147',
|
||||||
// 'open-keychain:safeslinger-exchange:a1cda62f799df6b416aca75d0e91d93d42c6171aab7da831086b522bfbef454f',
|
// 'OpenKeychain:safeslinger-exchange:99d6ce7745edf5341495932bb26d0d1f8894b1edbfbddffc8514b2cf4149ad9b',
|
||||||
'com.android.support:animated-vector-drawable:4fcd1fc36034a804200ef3e552b0f2f688a0a7a8a007de43201e40bfedda73b3',
|
'com.android.support:animated-vector-drawable:4fcd1fc36034a804200ef3e552b0f2f688a0a7a8a007de43201e40bfedda73b3',
|
||||||
'com.android.support:support-vector-drawable:45b1f180b437a750429f6c1457181c167ba211c17fcb992f83cdbefef5eb1519',
|
'com.android.support:support-vector-drawable:45b1f180b437a750429f6c1457181c167ba211c17fcb992f83cdbefef5eb1519',
|
||||||
'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266',
|
'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266',
|
||||||
'com.fidesmo:nordpol-core:3de58e850a00bba5b4d3a604d1399bcd89f695ea191ec0b03a57222e18062d15',
|
'com.fidesmo:nordpol-core:3de58e850a00bba5b4d3a604d1399bcd89f695ea191ec0b03a57222e18062d15',
|
||||||
|
'com.tonicartos:superslim:ca89b5c674660cc6918a8f8fd385065bffeee27983e0d33c7c2f0ad7b34d2d49',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ package org.sufficientlysecure.keychain.ui;
|
|||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.MatrixCursor;
|
import android.database.MatrixCursor;
|
||||||
import android.database.MergeCursor;
|
import android.database.MergeCursor;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -43,14 +45,15 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AbsListView.MultiChoiceModeListener;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
import android.widget.ViewAnimator;
|
import android.widget.ViewAnimator;
|
||||||
|
|
||||||
import com.getbase.floatingactionbutton.FloatingActionButton;
|
import com.getbase.floatingactionbutton.FloatingActionButton;
|
||||||
import com.getbase.floatingactionbutton.FloatingActionsMenu;
|
import com.getbase.floatingactionbutton.FloatingActionsMenu;
|
||||||
import com.tonicartos.superslim.LayoutManager;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
@@ -67,25 +70,26 @@ import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
|
|||||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
|
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
|
||||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.ContentDescriptionHint;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.KeySectionedListAdapter;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.adapter.CursorAdapter;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerFragment;
|
|
||||||
import org.sufficientlysecure.keychain.util.FabContainer;
|
import org.sufficientlysecure.keychain.util.FabContainer;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
||||||
|
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.HashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
||||||
* 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 LoaderFragment
|
||||||
implements SearchView.OnQueryTextListener,
|
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
||||||
LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
|
LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
|
||||||
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||||
|
|
||||||
@@ -93,6 +97,9 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
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;
|
||||||
|
|
||||||
|
private KeyListAdapter mAdapter;
|
||||||
|
private StickyListHeadersListView mStickyList;
|
||||||
|
|
||||||
// saves the mode object for multiselect, needed for reset at some point
|
// saves the mode object for multiselect, needed for reset at some point
|
||||||
private ActionMode mActionMode = null;
|
private ActionMode mActionMode = null;
|
||||||
|
|
||||||
@@ -110,107 +117,16 @@ 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
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.key_list_fragment, container, false);
|
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
||||||
|
View view = inflater.inflate(R.layout.key_list_fragment, getContainer());
|
||||||
|
|
||||||
|
mStickyList = (StickyListHeadersListView) view.findViewById(R.id.key_list_list);
|
||||||
|
mStickyList.setOnItemClickListener(this);
|
||||||
|
|
||||||
mFab = (FloatingActionsMenu) view.findViewById(R.id.fab_main);
|
mFab = (FloatingActionsMenu) view.findViewById(R.id.fab_main);
|
||||||
|
|
||||||
@@ -241,7 +157,7 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return view;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -254,13 +170,102 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
|
|
||||||
// show app name instead of "keys" from nav drawer
|
// show app name instead of "keys" from nav drawer
|
||||||
final FragmentActivity activity = getActivity();
|
final FragmentActivity activity = getActivity();
|
||||||
|
|
||||||
activity.setTitle(R.string.app_name);
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
// Start out with a progress indicator.
|
// Start out with a progress indicator.
|
||||||
hideList(false);
|
setContentShown(false);
|
||||||
|
|
||||||
|
// this view is made visible if no data is available
|
||||||
|
mStickyList.setEmptyView(activity.findViewById(R.id.key_list_empty));
|
||||||
|
|
||||||
// click on search button (in empty view) starts query for search string
|
// click on search button (in empty view) starts query for search string
|
||||||
vSearchContainer = (ViewAnimator) activity.findViewById(R.id.search_container);
|
vSearchContainer = (ViewAnimator) activity.findViewById(R.id.search_container);
|
||||||
@@ -273,13 +278,8 @@ 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);
|
||||||
|
mStickyList.setAdapter(mAdapter);
|
||||||
KeySectionedListAdapter adapter = new KeySectionedListAdapter(getContext(), null);
|
|
||||||
adapter.setKeyListener(mKeyListener);
|
|
||||||
|
|
||||||
setAdapter(adapter);
|
|
||||||
setLayoutManager(new LayoutManager(getActivity()));
|
|
||||||
|
|
||||||
// Prepare the loader. Either re-connect with an existing one,
|
// Prepare the loader. Either re-connect with an existing one,
|
||||||
// or start a new one.
|
// or start a new one.
|
||||||
@@ -298,6 +298,9 @@ 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
|
||||||
@@ -311,17 +314,31 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
|
|
||||||
// Now create and return a CursorLoader that will take care of
|
// Now create and return a CursorLoader that will take care of
|
||||||
// creating a Cursor for the data being displayed.
|
// creating a Cursor for the data being displayed.
|
||||||
return new CursorLoader(getActivity(), uri,
|
return new CursorLoader(getActivity(), uri, KeyListAdapter.PROJECTION, null, null, ORDER);
|
||||||
KeySectionedListAdapter.KeyListCursor.PROJECTION, null, null,
|
|
||||||
KeySectionedListAdapter.KeyListCursor.ORDER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||||
// 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);
|
mAdapter.setSearchQuery(mQuery);
|
||||||
getAdapter().swapCursor(KeySectionedListAdapter.KeyListCursor.wrap(data));
|
|
||||||
|
if (data != null && (mQuery == null || TextUtils.isEmpty(mQuery))) {
|
||||||
|
boolean isSecret = data.moveToFirst() && data.getInt(KeyListAdapter.INDEX_HAS_ANY_SECRET) != 0;
|
||||||
|
if (!isSecret) {
|
||||||
|
MatrixCursor headerCursor = new MatrixCursor(KeyListAdapter.PROJECTION);
|
||||||
|
Long[] row = new Long[KeyListAdapter.PROJECTION.length];
|
||||||
|
row[KeyListAdapter.INDEX_HAS_ANY_SECRET] = 1L;
|
||||||
|
row[KeyListAdapter.INDEX_MASTER_KEY_ID] = 0L;
|
||||||
|
headerCursor.addRow(row);
|
||||||
|
|
||||||
|
Cursor dataCursor = data;
|
||||||
|
data = new MergeCursor(new Cursor[] {
|
||||||
|
headerCursor, dataCursor
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mAdapter.swapCursor(data);
|
||||||
|
|
||||||
// end action mode, if any
|
// end action mode, if any
|
||||||
if (mActionMode != null) {
|
if (mActionMode != null) {
|
||||||
@@ -330,9 +347,9 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
|
|
||||||
// The list should now be shown.
|
// The list should now be shown.
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
showList(true);
|
setContentShown(true);
|
||||||
} else {
|
} else {
|
||||||
showList(false);
|
setContentShownNoAnimation(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,9 +358,47 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
// This is called when the last Cursor provided to onLoadFinished()
|
// This is called when the last Cursor provided to onLoadFinished()
|
||||||
// above is about to be closed. We need to make sure we are no
|
// above is about to be closed. We need to make sure we are no
|
||||||
// longer using it.
|
// longer using it.
|
||||||
getAdapter().swapCursor(null);
|
mAdapter.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(mAdapter.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);
|
||||||
@@ -358,6 +413,7 @@ 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
|
||||||
@@ -502,6 +558,7 @@ 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
|
||||||
@@ -516,7 +573,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);
|
||||||
@@ -535,6 +592,7 @@ 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>() {
|
||||||
|
|
||||||
@@ -564,11 +622,14 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mConsolidateOpHelper = new CryptoOperationHelper<>(2, this, callback, R.string.progress_importing);
|
mConsolidateOpHelper =
|
||||||
|
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>() {
|
||||||
|
|
||||||
@@ -598,7 +659,9 @@ public class KeyListFragment extends RecyclerFragment<KeySectionedListAdapter>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CryptoOperationHelper opHelper = new CryptoOperationHelper<>(2, this, callback, R.string.progress_importing);
|
CryptoOperationHelper opHelper =
|
||||||
|
new CryptoOperationHelper<>(2, this, callback, R.string.progress_importing);
|
||||||
|
|
||||||
opHelper.cryptoOperation();
|
opHelper.cryptoOperation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,7 +680,6 @@ 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();
|
||||||
@@ -689,4 +751,209 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.key_list_header, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -------------------------- MULTI-SELECTION METHODS --------------
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ public class KeyAdapter extends CursorAdapter {
|
|||||||
|
|
||||||
public static class KeyItemViewHolder {
|
public static class KeyItemViewHolder {
|
||||||
public View mView;
|
public View mView;
|
||||||
|
public View mLayoutDummy;
|
||||||
public View mLayoutData;
|
public View mLayoutData;
|
||||||
public Long mMasterKeyId;
|
public Long mMasterKeyId;
|
||||||
public TextView mMainUserId;
|
public TextView mMainUserId;
|
||||||
@@ -108,6 +109,7 @@ public class KeyAdapter extends CursorAdapter {
|
|||||||
public KeyItemViewHolder(View view) {
|
public KeyItemViewHolder(View view) {
|
||||||
mView = view;
|
mView = view;
|
||||||
mLayoutData = view.findViewById(R.id.key_list_item_data);
|
mLayoutData = view.findViewById(R.id.key_list_item_data);
|
||||||
|
mLayoutDummy = view.findViewById(R.id.key_list_item_dummy);
|
||||||
mMainUserId = (TextView) view.findViewById(R.id.key_list_item_name);
|
mMainUserId = (TextView) view.findViewById(R.id.key_list_item_name);
|
||||||
mMainUserIdRest = (TextView) view.findViewById(R.id.key_list_item_email);
|
mMainUserIdRest = (TextView) view.findViewById(R.id.key_list_item_email);
|
||||||
mStatus = (ImageView) view.findViewById(R.id.key_list_item_status_icon);
|
mStatus = (ImageView) view.findViewById(R.id.key_list_item_status_icon);
|
||||||
@@ -117,6 +119,10 @@ public class KeyAdapter extends CursorAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setData(Context context, KeyItem item, Highlighter highlighter, boolean enabled) {
|
public void setData(Context context, KeyItem item, Highlighter highlighter, boolean enabled) {
|
||||||
|
|
||||||
|
mLayoutData.setVisibility(View.VISIBLE);
|
||||||
|
mLayoutDummy.setVisibility(View.GONE);
|
||||||
|
|
||||||
mDisplayedItem = item;
|
mDisplayedItem = item;
|
||||||
|
|
||||||
{ // set name and stuff, common to both key types
|
{ // set name and stuff, common to both key types
|
||||||
@@ -201,8 +207,25 @@ public class KeyAdapter extends CursorAdapter {
|
|||||||
} else {
|
} else {
|
||||||
mCreationDate.setVisibility(View.GONE);
|
mCreationDate.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Shows the "you have no keys yet" dummy view, and sets an OnClickListener. */
|
||||||
|
public void setDummy(OnClickListener listener) {
|
||||||
|
|
||||||
|
// just reset everything to display the dummy layout
|
||||||
|
mLayoutDummy.setVisibility(View.VISIBLE);
|
||||||
|
mLayoutData.setVisibility(View.GONE);
|
||||||
|
mSlinger.setVisibility(View.GONE);
|
||||||
|
mStatus.setVisibility(View.GONE);
|
||||||
|
mView.setClickable(false);
|
||||||
|
|
||||||
|
mLayoutDummy.setOnClickListener(listener);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled(Cursor cursor) {
|
public boolean isEnabled(Cursor cursor) {
|
||||||
|
|||||||
@@ -1,553 +0,0 @@
|
|||||||
package org.sufficientlysecure.keychain.ui.adapter;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.MatrixCursor;
|
|
||||||
import android.database.MergeCursor;
|
|
||||||
import android.graphics.PorterDuff;
|
|
||||||
import android.support.v4.content.ContextCompat;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.adapter.*;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class KeySectionedListAdapter extends SectionCursorAdapter<KeySectionedListAdapter.KeyListCursor, Character,
|
|
||||||
SectionCursorAdapter.ViewHolder, KeySectionedListAdapter.KeyHeaderViewHolder> {
|
|
||||||
|
|
||||||
private static final short VIEW_ITEM_TYPE_KEY = 0x0;
|
|
||||||
private static final short VIEW_ITEM_TYPE_DUMMY = 0x1;
|
|
||||||
|
|
||||||
private static final short VIEW_SECTION_TYPE_PRIVATE = 0x0;
|
|
||||||
private static final short VIEW_SECTION_TYPE_PUBLIC = 0x1;
|
|
||||||
|
|
||||||
private String mQuery;
|
|
||||||
private List<Integer> mSelected;
|
|
||||||
private KeyListListener mListener;
|
|
||||||
|
|
||||||
private boolean mHasDummy = false;
|
|
||||||
|
|
||||||
public KeySectionedListAdapter(Context context, Cursor cursor) {
|
|
||||||
super(context, KeyListCursor.wrap(cursor, KeyListCursor.class), 0);
|
|
||||||
|
|
||||||
mQuery = "";
|
|
||||||
mSelected = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSearchQuery(String query) {
|
|
||||||
mQuery = query;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onContentChanged() {
|
|
||||||
mHasDummy = false;
|
|
||||||
mSelected.clear();
|
|
||||||
|
|
||||||
if(mListener != null) {
|
|
||||||
mListener.onSelectionStateChanged(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onContentChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeyListCursor swapCursor(KeyListCursor cursor) {
|
|
||||||
if (cursor != null && (mQuery == null || TextUtils.isEmpty(mQuery))) {
|
|
||||||
boolean isSecret = cursor.moveToFirst() && cursor.isSecret();
|
|
||||||
|
|
||||||
if (!isSecret) {
|
|
||||||
MatrixCursor headerCursor = new MatrixCursor(KeyListCursor.PROJECTION);
|
|
||||||
Long[] row = new Long[KeyListCursor.PROJECTION.length];
|
|
||||||
row[cursor.getColumnIndex(KeychainContract.KeyRings.HAS_ANY_SECRET)] = 1L;
|
|
||||||
row[cursor.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID)] = 0L;
|
|
||||||
headerCursor.addRow(row);
|
|
||||||
|
|
||||||
Cursor[] toMerge = {
|
|
||||||
headerCursor,
|
|
||||||
cursor.getWrappedCursor()
|
|
||||||
};
|
|
||||||
|
|
||||||
cursor = KeyListCursor.wrap(new MergeCursor(toMerge));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 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++) {
|
|
||||||
int index = getCursorPositionWithoutSections(mSelected.get(i));
|
|
||||||
if(!moveCursor(index)) {
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
keys[i] = getIdFromCursor(getCursor());
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAnySecretKeySelected() {
|
|
||||||
for(int i = 0; i < mSelected.size(); i++) {
|
|
||||||
int index = getCursorPositionWithoutSections(mSelected.get(i));
|
|
||||||
if(!moveCursor(index)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(getCursor().isSecret()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of database entries displayed.
|
|
||||||
* @return The item count
|
|
||||||
*/
|
|
||||||
public int getCount() {
|
|
||||||
if (getCursor() != null) {
|
|
||||||
return getCursor().getCount() - (mHasDummy ? 1 : 0);
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getIdFromCursor(KeyListCursor cursor) {
|
|
||||||
return cursor.getKeyId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Character getSectionFromCursor(KeyListCursor cursor) throws IllegalStateException {
|
|
||||||
if (cursor.isSecret()) {
|
|
||||||
if (cursor.getKeyId() == 0L) {
|
|
||||||
mHasDummy = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '#';
|
|
||||||
} else {
|
|
||||||
String userId = cursor.getRawUserId();
|
|
||||||
if(TextUtils.isEmpty(userId)) {
|
|
||||||
return '?';
|
|
||||||
} else {
|
|
||||||
return Character.toUpperCase(userId.charAt(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected short getSectionHeaderViewType(int sectionIndex) {
|
|
||||||
return (sectionIndex < 1) ?
|
|
||||||
VIEW_SECTION_TYPE_PRIVATE :
|
|
||||||
VIEW_SECTION_TYPE_PUBLIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected short getSectionItemViewType(int position) {
|
|
||||||
if (moveCursor(position)) {
|
|
||||||
KeyListCursor c = getCursor();
|
|
||||||
|
|
||||||
if (c.isSecret() && c.getKeyId() == 0L) {
|
|
||||||
return VIEW_ITEM_TYPE_DUMMY;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.w(Constants.TAG, "Unable to determine key view type. "
|
|
||||||
+ "Reason: Could not move cursor over dataset.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return VIEW_ITEM_TYPE_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected KeyHeaderViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
switch (viewType) {
|
|
||||||
case VIEW_SECTION_TYPE_PUBLIC:
|
|
||||||
return new KeyHeaderViewHolder(LayoutInflater.from(parent.getContext())
|
|
||||||
.inflate(R.layout.key_list_header_public, parent, false));
|
|
||||||
|
|
||||||
case VIEW_SECTION_TYPE_PRIVATE:
|
|
||||||
return new KeyHeaderViewHolder(LayoutInflater.from(parent.getContext())
|
|
||||||
.inflate(R.layout.key_list_header_private, parent, false));
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
switch (viewType) {
|
|
||||||
case VIEW_ITEM_TYPE_KEY:
|
|
||||||
return new KeyItemViewHolder(LayoutInflater.from(parent.getContext())
|
|
||||||
.inflate(R.layout.key_list_item, parent, false));
|
|
||||||
|
|
||||||
case VIEW_ITEM_TYPE_DUMMY:
|
|
||||||
return new KeyDummyViewHolder(LayoutInflater.from(parent.getContext())
|
|
||||||
.inflate(R.layout.key_list_dummy, parent, false));
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onBindSectionViewHolder(KeyHeaderViewHolder holder, Character section) {
|
|
||||||
switch (holder.getItemViewTypeWithoutSections()) {
|
|
||||||
case VIEW_SECTION_TYPE_PUBLIC: {
|
|
||||||
String title = section.equals('?') ?
|
|
||||||
getContext().getString(R.string.user_id_no_name) :
|
|
||||||
String.valueOf(section);
|
|
||||||
|
|
||||||
holder.bind(title);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case VIEW_SECTION_TYPE_PRIVATE: {
|
|
||||||
int count = getCount();
|
|
||||||
String title = getContext().getResources()
|
|
||||||
.getQuantityString(R.plurals.n_keys, count, count);
|
|
||||||
holder.bind(title);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onBindItemViewHolder(ViewHolder holder, KeyListCursor cursor) {
|
|
||||||
if (holder.getItemViewTypeWithoutSections() == VIEW_ITEM_TYPE_KEY) {
|
|
||||||
Highlighter highlighter = new Highlighter(getContext(), mQuery);
|
|
||||||
((KeyItemViewHolder) holder).bindKey(cursor, highlighter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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{
|
|
||||||
|
|
||||||
KeyDummyViewHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
|
|
||||||
itemView.setClickable(true);
|
|
||||||
itemView.setOnClickListener(this);
|
|
||||||
itemView.setEnabled(getSelectedCount() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
if(mListener != null) {
|
|
||||||
mListener.onKeyDummyItemClicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KeyItemViewHolder extends SectionCursorAdapter.ViewHolder
|
|
||||||
implements View.OnClickListener, View.OnLongClickListener {
|
|
||||||
|
|
||||||
private TextView mMainUserId;
|
|
||||||
private TextView mMainUserIdRest;
|
|
||||||
private TextView mCreationDate;
|
|
||||||
private ImageView mStatus;
|
|
||||||
private View mSlinger;
|
|
||||||
private ImageButton mSlingerButton;
|
|
||||||
|
|
||||||
KeyItemViewHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
|
|
||||||
mMainUserId = (TextView) itemView.findViewById(R.id.key_list_item_name);
|
|
||||||
mMainUserIdRest = (TextView) itemView.findViewById(R.id.key_list_item_email);
|
|
||||||
mStatus = (ImageView) itemView.findViewById(R.id.key_list_item_status_icon);
|
|
||||||
mSlinger = itemView.findViewById(R.id.key_list_item_slinger_view);
|
|
||||||
mSlingerButton = (ImageButton) itemView.findViewById(R.id.key_list_item_slinger_button);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bindKey(KeyListCursor keyItem, Highlighter highlighter) {
|
|
||||||
itemView.setSelected(isSelected(getAdapterPosition()));
|
|
||||||
Context context = itemView.getContext();
|
|
||||||
|
|
||||||
{ // set name and stuff, common to both key types
|
|
||||||
OpenPgpUtils.UserId userIdSplit = keyItem.getUserId();
|
|
||||||
if (userIdSplit.name != null) {
|
|
||||||
mMainUserId.setText(highlighter.highlight(userIdSplit.name));
|
|
||||||
} else {
|
|
||||||
mMainUserId.setText(R.string.user_id_no_name);
|
|
||||||
}
|
|
||||||
if (userIdSplit.email != null) {
|
|
||||||
mMainUserIdRest.setText(highlighter.highlight(userIdSplit.email));
|
|
||||||
mMainUserIdRest.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
mMainUserIdRest.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // set edit button and status, specific by key type. Note: order is important!
|
|
||||||
int textColor;
|
|
||||||
if (keyItem.isRevoked()) {
|
|
||||||
KeyFormattingUtils.setStatusImage(
|
|
||||||
context,
|
|
||||||
mStatus,
|
|
||||||
null,
|
|
||||||
KeyFormattingUtils.State.REVOKED,
|
|
||||||
R.color.key_flag_gray
|
|
||||||
);
|
|
||||||
|
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
|
||||||
mSlinger.setVisibility(View.GONE);
|
|
||||||
textColor = ContextCompat.getColor(context, R.color.key_flag_gray);
|
|
||||||
} else if (keyItem.isExpired()) {
|
|
||||||
KeyFormattingUtils.setStatusImage(
|
|
||||||
context,
|
|
||||||
mStatus,
|
|
||||||
null,
|
|
||||||
KeyFormattingUtils.State.EXPIRED,
|
|
||||||
R.color.key_flag_gray
|
|
||||||
);
|
|
||||||
|
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
|
||||||
mSlinger.setVisibility(View.GONE);
|
|
||||||
textColor = ContextCompat.getColor(context, R.color.key_flag_gray);
|
|
||||||
} else if (keyItem.isSecret()) {
|
|
||||||
mStatus.setVisibility(View.GONE);
|
|
||||||
if (mSlingerButton.hasOnClickListeners()) {
|
|
||||||
mSlingerButton.setColorFilter(
|
|
||||||
FormattingUtils.getColorFromAttr(context, R.attr.colorTertiaryText),
|
|
||||||
PorterDuff.Mode.SRC_IN
|
|
||||||
);
|
|
||||||
|
|
||||||
mSlinger.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
mSlinger.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
textColor = FormattingUtils.getColorFromAttr(context, R.attr.colorText);
|
|
||||||
} else {
|
|
||||||
// this is a public key - show if it's verified
|
|
||||||
if (keyItem.isVerified()) {
|
|
||||||
KeyFormattingUtils.setStatusImage(
|
|
||||||
context,
|
|
||||||
mStatus,
|
|
||||||
KeyFormattingUtils.State.VERIFIED
|
|
||||||
);
|
|
||||||
|
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
KeyFormattingUtils.setStatusImage(
|
|
||||||
context,
|
|
||||||
mStatus,
|
|
||||||
KeyFormattingUtils.State.UNVERIFIED
|
|
||||||
);
|
|
||||||
|
|
||||||
mStatus.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
mSlinger.setVisibility(View.GONE);
|
|
||||||
textColor = FormattingUtils.getColorFromAttr(context, R.attr.colorText);
|
|
||||||
}
|
|
||||||
|
|
||||||
mMainUserId.setTextColor(textColor);
|
|
||||||
mMainUserIdRest.setTextColor(textColor);
|
|
||||||
|
|
||||||
if (keyItem.hasDuplicate()) {
|
|
||||||
String dateTime = DateUtils.formatDateTime(context,
|
|
||||||
keyItem.getCreationTime(),
|
|
||||||
DateUtils.FORMAT_SHOW_DATE
|
|
||||||
| DateUtils.FORMAT_SHOW_TIME
|
|
||||||
| DateUtils.FORMAT_SHOW_YEAR
|
|
||||||
| DateUtils.FORMAT_ABBREV_MONTH);
|
|
||||||
mCreationDate.setText(context.getString(R.string.label_key_created,
|
|
||||||
dateTime));
|
|
||||||
mCreationDate.setTextColor(textColor);
|
|
||||||
mCreationDate.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
mCreationDate.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class KeyHeaderViewHolder extends SectionCursorAdapter.ViewHolder {
|
|
||||||
private TextView mText1;
|
|
||||||
|
|
||||||
public KeyHeaderViewHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
mText1 = (TextView) itemView.findViewById(android.R.id.text1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bind(String title) {
|
|
||||||
mText1.setText(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class KeyListCursor extends CursorAdapter.KeyCursor {
|
|
||||||
public static final String ORDER = KeychainContract.KeyRings.HAS_ANY_SECRET
|
|
||||||
+ " DESC, " + KeychainContract.KeyRings.USER_ID + " COLLATE NOCASE ASC";
|
|
||||||
|
|
||||||
public static final String[] PROJECTION;
|
|
||||||
|
|
||||||
static {
|
|
||||||
ArrayList<String> arr = new ArrayList<>();
|
|
||||||
arr.addAll(Arrays.asList(KeyCursor.PROJECTION));
|
|
||||||
arr.addAll(Arrays.asList(
|
|
||||||
KeychainContract.KeyRings.VERIFIED,
|
|
||||||
KeychainContract.KeyRings.HAS_ANY_SECRET,
|
|
||||||
KeychainContract.KeyRings.FINGERPRINT,
|
|
||||||
KeychainContract.KeyRings.HAS_ENCRYPT
|
|
||||||
));
|
|
||||||
|
|
||||||
PROJECTION = arr.toArray(new String[arr.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyListCursor wrap(Cursor cursor) {
|
|
||||||
if (cursor != null) {
|
|
||||||
return new KeyListCursor(cursor);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private KeyListCursor(Cursor cursor) {
|
|
||||||
super(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasEncrypt() {
|
|
||||||
int index = getColumnIndexOrThrow(KeychainContract.KeyRings.HAS_ENCRYPT);
|
|
||||||
return getInt(index) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getRawFingerprint() {
|
|
||||||
int index = getColumnIndexOrThrow(KeychainContract.KeyRings.FINGERPRINT);
|
|
||||||
return getBlob(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFingerprint() {
|
|
||||||
return KeyFormattingUtils.convertFingerprintToHex(getRawFingerprint());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSecret() {
|
|
||||||
int index = getColumnIndexOrThrow(KeychainContract.KeyRings.HAS_ANY_SECRET);
|
|
||||||
return getInt(index) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVerified() {
|
|
||||||
int index = getColumnIndexOrThrow(KeychainContract.KeyRings.VERIFIED);
|
|
||||||
return getInt(index) > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface KeyListListener {
|
|
||||||
void onKeyDummyItemClicked();
|
|
||||||
void onKeyItemClicked(long masterKeyId);
|
|
||||||
void onSlingerButtonClicked(long masterKeyId);
|
|
||||||
void onSelectionStateChanged(int selectedCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:focusable="true"
|
|
||||||
android:background="?android:selectableItemBackground">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="0dip"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:paddingRight="4dp"
|
|
||||||
android:paddingEnd="4dp"
|
|
||||||
android:paddingTop="4dp"
|
|
||||||
android:paddingBottom="4dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="You don't have any keys yet!"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="Click here to create or import one."
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:src="@drawable/ic_key_plus_grey600_24dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,144 +1,117 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:fab="http://schemas.android.com/apk/res-auto"
|
<RelativeLayout xmlns:fab="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:custom="http://schemas.android.com/apk/res-auto"
|
xmlns:custom="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
>
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
<!--rebuild functionality of ListFragment -->
|
||||||
android:layout_height="match_parent"
|
<FrameLayout
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@android:id/progress"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<ProgressBar
|
<se.emilsjolander.stickylistheaders.StickyListHeadersListView
|
||||||
style="?android:attr/progressBarStyleLarge"
|
android:id="@+id/key_list_list"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@android:id/widget_frame"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<!--rebuild functionality of ListFragment -->
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:drawSelectorOnTop="true"
|
||||||
|
android:fastScrollEnabled="true"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="32dp"
|
||||||
|
android:scrollbarStyle="outsideOverlay" />
|
||||||
|
|
||||||
<org.sufficientlysecure.keychain.ui.util.recyclerview.fastscroll.FastScrollRecyclerView
|
<LinearLayout
|
||||||
android:id="@android:id/list"
|
android:id="@+id/key_list_empty"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="240dp"
|
||||||
android:paddingLeft="16dp"
|
android:gravity="center"
|
||||||
android:paddingStart="16dp"
|
android:orientation="vertical"
|
||||||
android:paddingRight="32dp"
|
android:animateLayoutChanges="true"
|
||||||
android:paddingEnd="32dp"
|
>
|
||||||
android:paddingBottom="72dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
custom:fastScrollScrollBarWidth="8dp"
|
|
||||||
custom:fastScrollThumbColorNormal="@color/selected_gray"
|
|
||||||
custom:fastScrollThumbColorDragging="@color/accent" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:id="@android:id/empty"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="240dp"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical"
|
android:text="@string/key_list_empty_text1"
|
||||||
android:animateLayoutChanges="true" >
|
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||||
|
|
||||||
<TextView
|
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/search_container"
|
||||||
|
android:inAnimation="@anim/fade_in_delayed"
|
||||||
|
android:outAnimation="@anim/fade_out"
|
||||||
|
android:measureAllChildren="true"
|
||||||
|
custom:initialView="1">
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<Button
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:id="@+id/search_button"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/key_list_empty_text1"
|
tools:text="@string/btn_search_for_query"
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
/>
|
||||||
|
|
||||||
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
|
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/search_container"
|
|
||||||
android:inAnimation="@anim/fade_in_delayed"
|
|
||||||
android:outAnimation="@anim/fade_out"
|
|
||||||
android:measureAllChildren="true"
|
|
||||||
custom:initialView="1">
|
|
||||||
|
|
||||||
<Space
|
</LinearLayout>
|
||||||
android:layout_width="wrap_content"
|
</FrameLayout>
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<Button
|
<com.getbase.floatingactionbutton.FloatingActionsMenu
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/fab_main"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/search_button"
|
android:layout_alignParentRight="true"
|
||||||
android:gravity="center"
|
android:layout_alignParentEnd="true"
|
||||||
tools:text="@string/btn_search_for_query"/>
|
android:layout_alignParentBottom="true"
|
||||||
|
fab:fab_addButtonColorNormal="?attr/colorPrimary"
|
||||||
|
fab:fab_addButtonColorPressed="?attr/colorPrimaryDark"
|
||||||
|
fab:fab_addButtonSize="normal"
|
||||||
|
fab:fab_addButtonPlusIconColor="@color/icons"
|
||||||
|
fab:fab_expandDirection="up"
|
||||||
|
fab:fab_labelStyle="@style/FabMenuStyle"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
>
|
||||||
|
|
||||||
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
|
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fab_add_qr_code"
|
||||||
</LinearLayout>
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<com.getbase.floatingactionbutton.FloatingActionsMenu
|
|
||||||
android:id="@+id/fab_main"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentRight="true"
|
fab:fab_icon="@drawable/ic_qrcode_white_24dp"
|
||||||
android:layout_alignParentEnd="true"
|
fab:fab_colorNormal="?attr/colorPrimary"
|
||||||
android:layout_alignParentBottom="true"
|
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||||
fab:fab_addButtonColorNormal="?attr/colorPrimary"
|
fab:fab_title="@string/key_list_fab_qr_code"
|
||||||
fab:fab_addButtonColorPressed="?attr/colorPrimaryDark"
|
fab:fab_size="mini" />
|
||||||
fab:fab_addButtonSize="normal"
|
|
||||||
fab:fab_addButtonPlusIconColor="@color/icons"
|
|
||||||
fab:fab_expandDirection="up"
|
|
||||||
fab:fab_labelStyle="@style/FabMenuStyle"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:layout_marginEnd="16dp">
|
|
||||||
|
|
||||||
<com.getbase.floatingactionbutton.FloatingActionButton
|
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fab_add_qr_code"
|
android:id="@+id/fab_add_cloud"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
fab:fab_icon="@drawable/ic_qrcode_white_24dp"
|
fab:fab_icon="@drawable/ic_cloud_search_24dp"
|
||||||
fab:fab_colorNormal="?attr/colorPrimary"
|
fab:fab_colorNormal="?attr/colorPrimary"
|
||||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||||
fab:fab_title="@string/key_list_fab_qr_code"
|
fab:fab_title="@string/key_list_fab_search"
|
||||||
fab:fab_size="mini" />
|
fab:fab_size="mini" />
|
||||||
|
|
||||||
<com.getbase.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/fab_add_cloud"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
fab:fab_icon="@drawable/ic_cloud_search_24dp"
|
|
||||||
fab:fab_colorNormal="?attr/colorPrimary"
|
|
||||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
|
||||||
fab:fab_title="@string/key_list_fab_search"
|
|
||||||
fab:fab_size="mini" />
|
|
||||||
|
|
||||||
<com.getbase.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/fab_add_file"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
fab:fab_icon="@drawable/ic_folder_white_24dp"
|
|
||||||
fab:fab_colorNormal="?attr/colorPrimary"
|
|
||||||
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
|
||||||
fab:fab_title="@string/key_list_fab_import"
|
|
||||||
fab:fab_size="mini" />
|
|
||||||
|
|
||||||
</com.getbase.floatingactionbutton.FloatingActionsMenu>
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
|
<com.getbase.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fab_add_file"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
fab:fab_icon="@drawable/ic_folder_white_24dp"
|
||||||
|
fab:fab_colorNormal="?attr/colorPrimary"
|
||||||
|
fab:fab_colorPressed="?attr/colorPrimaryDark"
|
||||||
|
fab:fab_title="@string/key_list_fab_import"
|
||||||
|
fab:fab_size="mini" />
|
||||||
|
|
||||||
|
</com.getbase.floatingactionbutton.FloatingActionsMenu>
|
||||||
|
</RelativeLayout>
|
||||||
|
|||||||
27
OpenKeychain/src/main/res/layout/key_list_header.xml
Normal file
27
OpenKeychain/src/main/res/layout/key_list_header.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/SectionHeader"
|
||||||
|
android:id="@+id/stickylist_header_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start|left"
|
||||||
|
android:text="header text" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:text="key count"
|
||||||
|
android:id="@+id/contacts_num"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:textColor="@android:color/darker_gray" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:super="http://schemas.android.com/apk/lib-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?android:colorBackground"
|
|
||||||
|
|
||||||
super:slm_headerDisplay="sticky|inline"
|
|
||||||
super:slm_section_sectionManager="linear"
|
|
||||||
tools:ignore="ResAuto">
|
|
||||||
|
|
||||||
<TextView style="@style/SectionHeader"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="start|left"
|
|
||||||
android:text="@string/my_keys" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/text1"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:textColor="@android:color/darker_gray"
|
|
||||||
tools:text="11 Keys"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:super="http://schemas.android.com/apk/lib-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?android:colorBackground"
|
|
||||||
|
|
||||||
super:slm_headerDisplay="sticky|inline"
|
|
||||||
super:slm_section_sectionManager="linear"
|
|
||||||
tools:ignore="ResAuto">
|
|
||||||
|
|
||||||
<TextView style="@style/SectionHeader"
|
|
||||||
android:id="@android:id/text1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="start|left"
|
|
||||||
tools:text="A" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
@@ -1,18 +1,63 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="?attr/listPreferredItemHeight"
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:maxLines="1"
|
android:singleLine="true"
|
||||||
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
|
||||||
|
android:id="@+id/key_list_item_dummy"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:focusable="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:background="?android:selectableItemBackground"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="4dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="You don't have any keys yet!"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="Click here to create or import one."
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:src="@drawable/ic_key_plus_grey600_24dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/key_list_item_data"
|
android:id="@+id/key_list_item_data"
|
||||||
android:layout_width="0dip"
|
android:layout_width="0dip"
|
||||||
@@ -38,7 +83,7 @@
|
|||||||
android:id="@+id/key_list_item_email"
|
android:id="@+id/key_list_item_email"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="1"
|
android:singleLine="true"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
tools:text="user@example.com"
|
tools:text="user@example.com"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
@@ -47,11 +92,12 @@
|
|||||||
android:id="@+id/key_list_item_creation"
|
android:id="@+id/key_list_item_creation"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="1"
|
android:singleLine="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible"
|
tools:visibility="visible"
|
||||||
tools:text="Created on 10/10/2010 10:00" />
|
tools:text="Created on 10/10/2010 10:00"
|
||||||
|
/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -88,6 +134,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
tools:src="@drawable/status_signature_revoked_cutout_24dp" />
|
tools:src="@drawable/status_signature_revoked_cutout_24dp"
|
||||||
|
/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user