mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-14 04:45:04 -05:00
Merge pull request #403 from Valodim/master
unify public and secret key lists
This commit is contained in:
commit
fb1b0deaf5
@ -61,7 +61,7 @@
|
|||||||
android:theme="@style/KeychainTheme"
|
android:theme="@style/KeychainTheme"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.KeyListPublicActivity"
|
android:name=".ui.KeyListActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop">
|
||||||
@ -71,12 +71,6 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
|
||||||
android:name=".ui.KeyListSecretActivity"
|
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
|
||||||
android:label="@string/title_manage_secret_keys"
|
|
||||||
android:launchMode="singleTop">
|
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.EditKeyActivity"
|
android:name=".ui.EditKeyActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
@ -86,19 +80,19 @@
|
|||||||
android:name=".ui.ViewKeyActivity"
|
android:name=".ui.ViewKeyActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_key_details"
|
android:label="@string/title_key_details"
|
||||||
android:parentActivityName=".ui.KeyListPublicActivity">
|
android:parentActivityName=".ui.KeyListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".ui.KeyListPublicActivity" />
|
android:value=".ui.KeyListActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.ViewKeyActivityJB"
|
android:name=".ui.ViewKeyActivityJB"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_key_details"
|
android:label="@string/title_key_details"
|
||||||
android:parentActivityName=".ui.KeyListPublicActivity">
|
android:parentActivityName=".ui.KeyListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".ui.KeyListPublicActivity" />
|
android:value=".ui.KeyListActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.SelectPublicKeyActivity"
|
android:name=".ui.SelectPublicKeyActivity"
|
||||||
|
@ -100,6 +100,10 @@ public class KeychainContract {
|
|||||||
/** Use if a single item is returned */
|
/** Use if a single item is returned */
|
||||||
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring";
|
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring";
|
||||||
|
|
||||||
|
public static Uri buildUnifiedKeyRingsUri() {
|
||||||
|
return CONTENT_URI;
|
||||||
|
}
|
||||||
|
|
||||||
public static Uri buildPublicKeyRingsUri() {
|
public static Uri buildPublicKeyRingsUri() {
|
||||||
return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
|
return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,8 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
private static final int API_APPS_BY_ROW_ID = 302;
|
private static final int API_APPS_BY_ROW_ID = 302;
|
||||||
private static final int API_APPS_BY_PACKAGE_NAME = 303;
|
private static final int API_APPS_BY_PACKAGE_NAME = 303;
|
||||||
|
|
||||||
|
private static final int UNIFIED_KEY_RING = 401;
|
||||||
|
|
||||||
// private static final int DATA_STREAM = 401;
|
// private static final int DATA_STREAM = 401;
|
||||||
|
|
||||||
protected UriMatcher mUriMatcher;
|
protected UriMatcher mUriMatcher;
|
||||||
@ -94,6 +96,15 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
|
|
||||||
String authority = KeychainContract.CONTENT_AUTHORITY;
|
String authority = KeychainContract.CONTENT_AUTHORITY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unified key rings
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* key_rings
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS, UNIFIED_KEY_RING);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* public key rings
|
* public key rings
|
||||||
*
|
*
|
||||||
@ -365,6 +376,10 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
|
|
||||||
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
|
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
|
||||||
|
|
||||||
|
// type attribute is special: if there is any grouping, choose secret over public type
|
||||||
|
projectionMap.put(KeyRingsColumns.TYPE,
|
||||||
|
"MAX(" + Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + ") AS " + KeyRingsColumns.TYPE);
|
||||||
|
|
||||||
return projectionMap;
|
return projectionMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,9 +414,11 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
* Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
|
* Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
|
||||||
*/
|
*/
|
||||||
private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) {
|
private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) {
|
||||||
// public or secret keyring
|
if(match != UNIFIED_KEY_RING) {
|
||||||
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
|
// public or secret keyring
|
||||||
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
|
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
|
||||||
|
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
|
||||||
|
}
|
||||||
|
|
||||||
// join keyrings with keys and userIds
|
// join keyrings with keys and userIds
|
||||||
// Only get user id and key with rank 0 (main user id and main key)
|
// Only get user id and key with rank 0 (main user id and main key)
|
||||||
@ -455,7 +472,22 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
|
|
||||||
int match = mUriMatcher.match(uri);
|
int match = mUriMatcher.match(uri);
|
||||||
|
|
||||||
|
// all query() parameters, for good measure
|
||||||
|
String groupBy = null, having = null;
|
||||||
|
|
||||||
switch (match) {
|
switch (match) {
|
||||||
|
case UNIFIED_KEY_RING:
|
||||||
|
qb = buildKeyRingQuery(qb, match);
|
||||||
|
|
||||||
|
// GROUP BY so we don't get duplicates
|
||||||
|
groupBy = Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID;
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(sortOrder)) {
|
||||||
|
sortOrder = KeyRings.TYPE + " DESC, " + Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case PUBLIC_KEY_RING:
|
case PUBLIC_KEY_RING:
|
||||||
case SECRET_KEY_RING:
|
case SECRET_KEY_RING:
|
||||||
qb = buildKeyRingQuery(qb, match);
|
qb = buildKeyRingQuery(qb, match);
|
||||||
@ -630,7 +662,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
orderBy = sortOrder;
|
orderBy = sortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
|
Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy);
|
||||||
|
|
||||||
// Tell the cursor what uri to watch, so it knows when its source data changes
|
// Tell the cursor what uri to watch, so it knows when its source data changes
|
||||||
c.setNotificationUri(getContext().getContentResolver(), uri);
|
c.setNotificationUri(getContext().getContentResolver(), uri);
|
||||||
|
@ -483,13 +483,13 @@ public class ProviderHelper {
|
|||||||
*/
|
*/
|
||||||
public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) {
|
public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) {
|
||||||
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
|
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
|
||||||
return getMasterKeyCanSign(context, queryUri, keyRingRowId);
|
return getMasterKeyCanSign(context, queryUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private helper method to get master key private empty status of keyring by its row id
|
* Private helper method to get master key private empty status of keyring by its row id
|
||||||
*/
|
*/
|
||||||
private static boolean getMasterKeyCanSign(Context context, Uri queryUri, long keyRingRowId) {
|
public static boolean getMasterKeyCanSign(Context context, Uri queryUri) {
|
||||||
String[] projection = new String[]{
|
String[] projection = new String[]{
|
||||||
KeyRings.MASTER_KEY_ID,
|
KeyRings.MASTER_KEY_ID,
|
||||||
"(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS
|
"(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS
|
||||||
@ -515,6 +515,12 @@ public class ProviderHelper {
|
|||||||
return (masterKeyId > 0);
|
return (masterKeyId > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasSecretKeyByMasterKeyId(Context context, long masterKeyId) {
|
||||||
|
Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
|
||||||
|
// see if we can get our master key id back from the uri
|
||||||
|
return getMasterKeyId(context, queryUri) == masterKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get master key id of keyring by its row id
|
* Get master key id of keyring by its row id
|
||||||
*/
|
*/
|
||||||
@ -546,6 +552,26 @@ public class ProviderHelper {
|
|||||||
return masterKeyId;
|
return masterKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long getRowId(Context context, Uri queryUri) {
|
||||||
|
String[] projection = new String[]{KeyRings._ID};
|
||||||
|
Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
|
||||||
|
|
||||||
|
long rowId = 0;
|
||||||
|
try {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
int idCol = cursor.getColumnIndexOrThrow(KeyRings._ID);
|
||||||
|
|
||||||
|
rowId = cursor.getLong(idCol);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get fingerprint of key
|
* Get fingerprint of key
|
||||||
*/
|
*/
|
||||||
|
@ -50,9 +50,9 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
private CharSequence mDrawerTitle;
|
private CharSequence mDrawerTitle;
|
||||||
private CharSequence mTitle;
|
private CharSequence mTitle;
|
||||||
|
|
||||||
private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class,
|
private static Class[] mItemsClass = new Class[] { KeyListActivity.class,
|
||||||
EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
|
EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
|
||||||
KeyListSecretActivity.class, RegisteredAppsListActivity.class };
|
RegisteredAppsListActivity.class };
|
||||||
private Class mSelectedItem;
|
private Class mSelectedItem;
|
||||||
|
|
||||||
private static final int MENU_ID_PREFERENCE = 222;
|
private static final int MENU_ID_PREFERENCE = 222;
|
||||||
@ -72,7 +72,6 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
new NavItem("fa-lock", getString(R.string.nav_encrypt)),
|
new NavItem("fa-lock", getString(R.string.nav_encrypt)),
|
||||||
new NavItem("fa-unlock", getString(R.string.nav_decrypt)),
|
new NavItem("fa-unlock", getString(R.string.nav_decrypt)),
|
||||||
new NavItem("fa-download", getString(R.string.nav_import)),
|
new NavItem("fa-download", getString(R.string.nav_import)),
|
||||||
new NavItem("fa-key", getString(R.string.nav_secret_keys)),
|
|
||||||
new NavItem("fa-android", getString(R.string.nav_apps)) };
|
new NavItem("fa-android", getString(R.string.nav_apps)) };
|
||||||
|
|
||||||
mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
|
mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
|
||||||
|
@ -266,12 +266,10 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
} else {
|
} else {
|
||||||
Log.d(Constants.TAG, "uri: " + mDataUri);
|
Log.d(Constants.TAG, "uri: " + mDataUri);
|
||||||
|
|
||||||
long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
|
|
||||||
|
|
||||||
// get master key id using row id
|
// get master key id using row id
|
||||||
long masterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId);
|
long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
|
||||||
|
|
||||||
masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(this, keyRingRowId);
|
masterCanSign = ProviderHelper.getMasterKeyCanSign(this, mDataUri);
|
||||||
finallyEdit(masterKeyId, masterCanSign);
|
finallyEdit(masterKeyId, masterCanSign);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import android.os.Bundle;
|
|||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
public class KeyListPublicActivity extends DrawerActivity {
|
public class KeyListActivity extends DrawerActivity {
|
||||||
|
|
||||||
ExportHelper mExportHelper;
|
ExportHelper mExportHelper;
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ public class KeyListPublicActivity extends DrawerActivity {
|
|||||||
|
|
||||||
mExportHelper = new ExportHelper(this);
|
mExportHelper = new ExportHelper(this);
|
||||||
|
|
||||||
setContentView(R.layout.key_list_public_activity);
|
setContentView(R.layout.key_list_activity);
|
||||||
|
|
||||||
// now setup navigation drawer in DrawerActivity...
|
// now setup navigation drawer in DrawerActivity...
|
||||||
setupDrawerNavigation(savedInstanceState);
|
setupDrawerNavigation(savedInstanceState);
|
||||||
@ -46,19 +46,20 @@ public class KeyListPublicActivity extends DrawerActivity {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
getMenuInflater().inflate(R.menu.key_list_public, menu);
|
getMenuInflater().inflate(R.menu.key_list, menu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.menu_key_list_public_import:
|
case R.id.menu_key_list_import:
|
||||||
Intent intentImport = new Intent(this, ImportKeysActivity.class);
|
Intent intentImport = new Intent(this, ImportKeysActivity.class);
|
||||||
startActivityForResult(intentImport, 0);
|
startActivityForResult(intentImport, 0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_key_list_public_export:
|
case R.id.menu_key_list_export:
|
||||||
|
// TODO fix this for unified keylist
|
||||||
mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
|
mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
|
||||||
|
|
||||||
return true;
|
return true;
|
@ -17,26 +17,36 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
|
import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
|
import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
|
||||||
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||||
|
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
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;
|
||||||
@ -47,10 +57,13 @@ import android.support.v4.app.Fragment;
|
|||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
|
import android.support.v4.widget.CursorAdapter;
|
||||||
import android.support.v4.view.MenuItemCompat;
|
import android.support.v4.view.MenuItemCompat;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.support.v7.widget.SearchView;
|
import android.support.v7.widget.SearchView;
|
||||||
|
import android.text.Spannable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@ -62,7 +75,9 @@ import android.view.ViewGroup;
|
|||||||
import android.view.animation.AnimationUtils;
|
import android.view.animation.AnimationUtils;
|
||||||
import android.widget.AbsListView.MultiChoiceModeListener;
|
import android.widget.AbsListView.MultiChoiceModeListener;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
@ -71,10 +86,10 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
|
|||||||
* 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 KeyListPublicFragment extends Fragment implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
public class KeyListFragment extends Fragment implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
||||||
LoaderManager.LoaderCallbacks<Cursor> {
|
LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
private KeyListPublicAdapter mAdapter;
|
private KeyListAdapter mAdapter;
|
||||||
private StickyListHeadersListView mStickyList;
|
private StickyListHeadersListView mStickyList;
|
||||||
|
|
||||||
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||||
@ -94,9 +109,9 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View root = inflater.inflate(R.layout.key_list_public_fragment, container, false);
|
View root = inflater.inflate(R.layout.key_list_fragment, container, false);
|
||||||
|
|
||||||
mStickyList = (StickyListHeadersListView) root.findViewById(R.id.key_list_public_list);
|
mStickyList = (StickyListHeadersListView) root.findViewById(R.id.key_list_list);
|
||||||
mStickyList.setOnItemClickListener(this);
|
mStickyList.setOnItemClickListener(this);
|
||||||
|
|
||||||
|
|
||||||
@ -125,8 +140,8 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
});
|
});
|
||||||
|
|
||||||
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||||
mListContainer = root.findViewById(R.id.key_list_public_list_container);
|
mListContainer = root.findViewById(R.id.key_list_list_container);
|
||||||
mProgressContainer = root.findViewById(R.id.key_list_public_progress_container);
|
mProgressContainer = root.findViewById(R.id.key_list_progress_container);
|
||||||
mListShown = true;
|
mListShown = true;
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
@ -140,6 +155,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
|
mStickyList.setOnItemClickListener(this);
|
||||||
mStickyList.setAreHeadersSticky(true);
|
mStickyList.setAreHeadersSticky(true);
|
||||||
mStickyList.setDrawingListUnderStickyHeader(false);
|
mStickyList.setDrawingListUnderStickyHeader(false);
|
||||||
mStickyList.setFastScrollEnabled(true);
|
mStickyList.setFastScrollEnabled(true);
|
||||||
@ -149,7 +165,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this view is made visible if no data is available
|
// this view is made visible if no data is available
|
||||||
mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_public_empty));
|
mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
|
* ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
|
||||||
@ -162,7 +178,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
@Override
|
@Override
|
||||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||||
android.view.MenuInflater inflater = getActivity().getMenuInflater();
|
android.view.MenuInflater inflater = getActivity().getMenuInflater();
|
||||||
inflater.inflate(R.menu.key_list_public_multi, menu);
|
inflater.inflate(R.menu.key_list_multi, menu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,25 +189,30 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
// get row ids for checked positions as long array
|
|
||||||
long[] ids = mStickyList.getCheckedItemIds();
|
// get IDs for checked positions as long array
|
||||||
|
long[] ids;
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.menu_key_list_public_multi_encrypt: {
|
case R.id.menu_key_list_multi_encrypt: {
|
||||||
|
ids = mAdapter.getCurrentSelectedMasterKeyIds();
|
||||||
encrypt(mode, ids);
|
encrypt(mode, ids);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.menu_key_list_public_multi_export: {
|
case R.id.menu_key_list_multi_delete: {
|
||||||
|
ids = mStickyList.getWrappedList().getCheckedItemIds();
|
||||||
|
showDeleteKeyDialog(mode, ids);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case R.id.menu_key_list_multi_export: {
|
||||||
|
// todo: public/secret needs to be handled differently here
|
||||||
|
ids = mStickyList.getWrappedList().getCheckedItemIds();
|
||||||
ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
|
ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
|
||||||
mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
|
mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.menu_key_list_public_multi_delete: {
|
case R.id.menu_key_list_multi_select_all: {
|
||||||
showDeleteKeyDialog(mode, ids);
|
// select all
|
||||||
break;
|
|
||||||
}
|
|
||||||
case R.id.menu_key_list_public_multi_select_all: {
|
|
||||||
//Select all
|
|
||||||
for (int i = 0; i < mStickyList.getCount(); i++) {
|
for (int i = 0; i < mStickyList.getCount(); i++) {
|
||||||
mStickyList.setItemChecked(i, true);
|
mStickyList.setItemChecked(i, true);
|
||||||
}
|
}
|
||||||
@ -231,7 +252,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
setListShown(false);
|
setListShown(false);
|
||||||
|
|
||||||
// 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 KeyListPublicAdapter(getActivity(), null, Id.type.public_key, USER_ID_INDEX);
|
mAdapter = new KeyListAdapter(getActivity(), null, Id.type.public_key);
|
||||||
mStickyList.setAdapter(mAdapter);
|
mStickyList.setAdapter(mAdapter);
|
||||||
|
|
||||||
// Prepare the loader. Either re-connect with an existing one,
|
// Prepare the loader. Either re-connect with an existing one,
|
||||||
@ -242,20 +263,25 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
// These are the rows that we will retrieve.
|
// These are the rows that we will retrieve.
|
||||||
static final String[] PROJECTION = new String[]{
|
static final String[] PROJECTION = new String[]{
|
||||||
KeychainContract.KeyRings._ID,
|
KeychainContract.KeyRings._ID,
|
||||||
|
KeychainContract.KeyRings.TYPE,
|
||||||
KeychainContract.KeyRings.MASTER_KEY_ID,
|
KeychainContract.KeyRings.MASTER_KEY_ID,
|
||||||
KeychainContract.UserIds.USER_ID,
|
KeychainContract.UserIds.USER_ID,
|
||||||
KeychainContract.Keys.IS_REVOKED
|
KeychainContract.Keys.IS_REVOKED
|
||||||
};
|
};
|
||||||
|
|
||||||
static final int USER_ID_INDEX = 2;
|
static final int INDEX_TYPE = 1;
|
||||||
|
static final int INDEX_UID = 3;
|
||||||
static final String SORT_ORDER = UserIds.USER_ID + " ASC";
|
static final String SORT_ORDER =
|
||||||
|
// show secret before public key
|
||||||
|
KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings.TYPE + " DESC, "
|
||||||
|
// sort by user id otherwise
|
||||||
|
+ UserIds.USER_ID + " 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
|
||||||
// sample only has one Loader, so we don't care about the ID.
|
// sample only has one Loader, so we don't care about the ID.
|
||||||
Uri baseUri = KeyRings.buildPublicKeyRingsUri();
|
Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
|
||||||
String where = null;
|
String where = null;
|
||||||
String whereArgs[] = null;
|
String whereArgs[] = null;
|
||||||
if (mCurQuery != null) {
|
if (mCurQuery != null) {
|
||||||
@ -273,6 +299,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
// old cursor once we return.)
|
// old cursor once we return.)
|
||||||
mAdapter.setSearchQuery(mCurQuery);
|
mAdapter.setSearchQuery(mCurQuery);
|
||||||
mAdapter.swapCursor(data);
|
mAdapter.swapCursor(data);
|
||||||
|
|
||||||
mStickyList.setAdapter(mAdapter);
|
mStickyList.setAdapter(mAdapter);
|
||||||
|
|
||||||
// NOTE: Not supported by StickyListHeader, but reimplemented here
|
// NOTE: Not supported by StickyListHeader, but reimplemented here
|
||||||
@ -303,21 +330,15 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
} else {
|
} else {
|
||||||
viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class);
|
viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class);
|
||||||
}
|
}
|
||||||
viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id)));
|
viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(mAdapter.getMasterKeyId(position))));
|
||||||
startActivity(viewIntent);
|
startActivity(viewIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(11)
|
@TargetApi(11)
|
||||||
public void encrypt(ActionMode mode, long[] keyRingRowIds) {
|
protected void encrypt(ActionMode mode, long[] keyRingMasterKeyIds) {
|
||||||
// get master key ids from row ids
|
|
||||||
long[] keyRingIds = new long[keyRingRowIds.length];
|
|
||||||
for (int i = 0; i < keyRingRowIds.length; i++) {
|
|
||||||
keyRingIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), keyRingRowIds[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent intent = new Intent(getActivity(), EncryptActivity.class);
|
Intent intent = new Intent(getActivity(), EncryptActivity.class);
|
||||||
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
|
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
|
||||||
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds);
|
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingMasterKeyIds);
|
||||||
// used instead of startActivity set actionbar based on callingPackage
|
// used instead of startActivity set actionbar based on callingPackage
|
||||||
startActivityForResult(intent, 0);
|
startActivityForResult(intent, 0);
|
||||||
|
|
||||||
@ -330,6 +351,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
* @param keyRingRowIds
|
* @param keyRingRowIds
|
||||||
*/
|
*/
|
||||||
@TargetApi(11)
|
@TargetApi(11)
|
||||||
|
// TODO: this method needs an overhaul to handle both public and secret keys gracefully!
|
||||||
public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
|
public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
|
||||||
// Message is received after key is deleted
|
// Message is received after key is deleted
|
||||||
Handler returnHandler = new Handler() {
|
Handler returnHandler = new Handler() {
|
||||||
@ -368,7 +390,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||||
// Get the searchview
|
// Get the searchview
|
||||||
MenuItem searchItem = menu.findItem(R.id.menu_key_list_public_search);
|
MenuItem searchItem = menu.findItem(R.id.menu_key_list_search);
|
||||||
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||||
|
|
||||||
// Execute this when searching
|
// Execute this when searching
|
||||||
@ -385,7 +407,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
public boolean onMenuItemActionCollapse(MenuItem item) {
|
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||||
mCurQuery = null;
|
mCurQuery = null;
|
||||||
mSearchView.setQuery("", true);
|
mSearchView.setQuery("", true);
|
||||||
getLoaderManager().restartLoader(0, null, KeyListPublicFragment.this);
|
getLoaderManager().restartLoader(0, null, KeyListFragment.this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -444,4 +466,263 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
|
|||||||
public void setListShownNoAnimation(boolean shown) {
|
public void setListShownNoAnimation(boolean shown) {
|
||||||
setListShown(shown, false);
|
setListShown(shown, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements StickyListHeadersAdapter from library
|
||||||
|
*/
|
||||||
|
private class KeyListAdapter extends HighlightQueryCursorAdapter implements StickyListHeadersAdapter {
|
||||||
|
private LayoutInflater mInflater;
|
||||||
|
private int mIndexUserId;
|
||||||
|
private int mIndexIsRevoked;
|
||||||
|
private int mMasterKeyId;
|
||||||
|
|
||||||
|
private String mCurQuery;
|
||||||
|
|
||||||
|
@SuppressLint("UseSparseArrays")
|
||||||
|
private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
|
||||||
|
|
||||||
|
public KeyListAdapter(Context context, Cursor c, int flags) {
|
||||||
|
super(context, c, flags);
|
||||||
|
|
||||||
|
mInflater = LayoutInflater.from(context);
|
||||||
|
initIndex(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor swapCursor(Cursor newCursor) {
|
||||||
|
initIndex(newCursor);
|
||||||
|
|
||||||
|
return super.swapCursor(newCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get column indexes for performance reasons just once in constructor and swapCursor. For a
|
||||||
|
* performance comparison see http://stackoverflow.com/a/17999582
|
||||||
|
*
|
||||||
|
* @param cursor
|
||||||
|
*/
|
||||||
|
private void initIndex(Cursor cursor) {
|
||||||
|
if (cursor != null) {
|
||||||
|
mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID);
|
||||||
|
mIndexIsRevoked = cursor.getColumnIndexOrThrow(KeychainContract.Keys.IS_REVOKED);
|
||||||
|
mMasterKeyId = cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.MASTER_KEY_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind cursor data to the item list view
|
||||||
|
* <p/>
|
||||||
|
* NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus
|
||||||
|
* no ViewHolder is required here.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
|
||||||
|
{ // set name and stuff, common to both key types
|
||||||
|
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
|
||||||
|
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
||||||
|
|
||||||
|
String userId = cursor.getString(mIndexUserId);
|
||||||
|
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
||||||
|
if (userIdSplit[0] != null) {
|
||||||
|
mainUserId.setText(highlightSearchQuery(userIdSplit[0]));
|
||||||
|
} else {
|
||||||
|
mainUserId.setText(R.string.user_id_no_name);
|
||||||
|
}
|
||||||
|
if (userIdSplit[1] != null) {
|
||||||
|
mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1]));
|
||||||
|
mainUserIdRest.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
mainUserIdRest.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // set edit button and revoked info, specific by key type
|
||||||
|
Button button = (Button) view.findViewById(R.id.edit);
|
||||||
|
TextView revoked = (TextView) view.findViewById(R.id.revoked);
|
||||||
|
|
||||||
|
if(cursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) {
|
||||||
|
// this is a secret key - show the edit button
|
||||||
|
revoked.setVisibility(View.GONE);
|
||||||
|
button.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
final long id = cursor.getLong(mMasterKeyId);
|
||||||
|
button.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
|
||||||
|
editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(id)));
|
||||||
|
editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
|
||||||
|
startActivityForResult(editIntent, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// this is a public key - hide the edit button, show if it's revoked
|
||||||
|
button.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
boolean isRevoked = cursor.getInt(mIndexIsRevoked) > 0;
|
||||||
|
revoked.setVisibility(isRevoked ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMasterKeyId(int id) {
|
||||||
|
|
||||||
|
if (!mCursor.moveToPosition(id)) {
|
||||||
|
throw new IllegalStateException("couldn't move cursor to position " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mCursor.getLong(mMasterKeyId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeyType(int position) {
|
||||||
|
|
||||||
|
if (!mCursor.moveToPosition(position)) {
|
||||||
|
throw new IllegalStateException("couldn't move cursor to position " + position);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mCursor.getInt(KeyListFragment.INDEX_TYPE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return mInflater.inflate(R.layout.key_list_item, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new header view and binds the section headers to it. It uses the ViewHolder
|
||||||
|
* pattern. Most functionality is similar to getView() from Android's CursorAdapter.
|
||||||
|
* <p/>
|
||||||
|
* NOTE: The variables mDataValid and mCursor are available due to the super class
|
||||||
|
* CursorAdapter.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public View getHeaderView(int position, View convertView, ViewGroup parent) {
|
||||||
|
HeaderViewHolder holder;
|
||||||
|
if (convertView == null) {
|
||||||
|
holder = new HeaderViewHolder();
|
||||||
|
convertView = mInflater.inflate(R.layout.key_list_header, parent, false);
|
||||||
|
holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text);
|
||||||
|
holder.count = (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(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) {
|
||||||
|
{ // set contact count
|
||||||
|
int num = mCursor.getCount();
|
||||||
|
String contactsTotal = getResources().getQuantityString(R.plurals.n_contacts, num, num);
|
||||||
|
holder.count.setText(contactsTotal);
|
||||||
|
holder.count.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.text.setText(convertView.getResources().getString(R.string.my_keys));
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set header text as first char in user id
|
||||||
|
String userId = mCursor.getString(KeyListFragment.INDEX_UID);
|
||||||
|
String headerText = convertView.getResources().getString(R.string.user_id_no_name);
|
||||||
|
if (userId != null && userId.length() > 0) {
|
||||||
|
headerText = "" + mCursor.getString(KeyListFragment.INDEX_UID).subSequence(0, 1).charAt(0);
|
||||||
|
}
|
||||||
|
holder.text.setText(headerText);
|
||||||
|
holder.count.setVisibility(View.GONE);
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header IDs should be static, position=1 should always return the same Id that is.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getHeaderId(int position) {
|
||||||
|
if (!mDataValid) {
|
||||||
|
// no data available at this point
|
||||||
|
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mCursor.moveToPosition(position)) {
|
||||||
|
throw new IllegalStateException("couldn't move cursor to position " + position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// early breakout: all secret keys are assigned id 0
|
||||||
|
if(mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET)
|
||||||
|
return 1L;
|
||||||
|
|
||||||
|
// otherwise, return the first character of the name as ID
|
||||||
|
String userId = mCursor.getString(KeyListFragment.INDEX_UID);
|
||||||
|
if (userId != null && userId.length() > 0) {
|
||||||
|
return userId.charAt(0);
|
||||||
|
} else {
|
||||||
|
return Long.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HeaderViewHolder {
|
||||||
|
TextView text;
|
||||||
|
TextView count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -------------------------- MULTI-SELECTION METHODS --------------
|
||||||
|
*/
|
||||||
|
public void setNewSelection(int position, boolean value) {
|
||||||
|
mSelection.put(position, value);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] getCurrentSelectedMasterKeyIds() {
|
||||||
|
long[] ids = new long[mSelection.size()];
|
||||||
|
int i = 0;
|
||||||
|
// get master key ids
|
||||||
|
for (int pos : mSelection.keySet())
|
||||||
|
ids[i++] = mAdapter.getMasterKeyId(pos);
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSelection(int position) {
|
||||||
|
mSelection.remove(position);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearSelection() {
|
||||||
|
mSelection.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change color for multi-selection
|
||||||
|
*/
|
||||||
|
// default color
|
||||||
|
if (mSelection.get(position) != null) {
|
||||||
|
// this is a selected position, change color!
|
||||||
|
v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
|
||||||
|
} else {
|
||||||
|
v.setBackgroundColor(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.Id;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
public class KeyListSecretActivity extends DrawerActivity {
|
|
||||||
|
|
||||||
ExportHelper mExportHelper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
mExportHelper = new ExportHelper(this);
|
|
||||||
|
|
||||||
setContentView(R.layout.key_list_secret_activity);
|
|
||||||
|
|
||||||
// now setup navigation drawer in DrawerActivity...
|
|
||||||
setupDrawerNavigation(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
super.onCreateOptionsMenu(menu);
|
|
||||||
getMenuInflater().inflate(R.menu.key_list_secret, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.menu_key_list_secret_create:
|
|
||||||
createKey();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
case R.id.menu_key_list_secret_create_expert:
|
|
||||||
createKeyExpert();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
case R.id.menu_key_list_secret_export:
|
|
||||||
mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.path.APP_DIR_FILE_SEC);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
case R.id.menu_key_list_secret_import:
|
|
||||||
Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
|
|
||||||
intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
|
|
||||||
startActivityForResult(intentImportFromFile, 0);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createKey() {
|
|
||||||
Intent intent = new Intent(this, EditKeyActivity.class);
|
|
||||||
intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
|
|
||||||
intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
|
|
||||||
intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view
|
|
||||||
startActivityForResult(intent, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createKeyExpert() {
|
|
||||||
Intent intent = new Intent(this, EditKeyActivity.class);
|
|
||||||
intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
|
|
||||||
startActivityForResult(intent, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,232 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.Id;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter;
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.Messenger;
|
|
||||||
import android.support.v4.app.ListFragment;
|
|
||||||
import android.support.v4.app.LoaderManager;
|
|
||||||
import android.support.v4.content.CursorLoader;
|
|
||||||
import android.support.v4.content.Loader;
|
|
||||||
import android.support.v7.app.ActionBarActivity;
|
|
||||||
import android.view.ActionMode;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.AbsListView.MultiChoiceModeListener;
|
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
|
||||||
|
|
||||||
public class KeyListSecretFragment extends ListFragment implements
|
|
||||||
LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener {
|
|
||||||
|
|
||||||
private KeyListSecretAdapter mAdapter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define Adapter and Loader on create of Activity
|
|
||||||
*/
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
|
||||||
super.onActivityCreated(savedInstanceState);
|
|
||||||
|
|
||||||
getListView().setOnItemClickListener(this);
|
|
||||||
|
|
||||||
// Give some text to display if there is no data. In a real
|
|
||||||
// application this would come from a resource.
|
|
||||||
setEmptyText(getString(R.string.list_empty));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
|
|
||||||
* available for Android >= 3.0
|
|
||||||
*/
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
|
||||||
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
|
||||||
getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
|
||||||
android.view.MenuInflater inflater = getActivity().getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.key_list_secret_multi, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
|
||||||
// get row ids for checked positions as long array
|
|
||||||
long[] ids = getListView().getCheckedItemIds();
|
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.menu_key_list_public_multi_delete: {
|
|
||||||
showDeleteKeyDialog(mode, ids);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case R.id.menu_key_list_public_multi_select_all: {
|
|
||||||
//Select all
|
|
||||||
for (int i = 0; i < getListView().getCount(); i++) {
|
|
||||||
getListView().setItemChecked(i, true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case R.id.menu_key_list_public_multi_export: {
|
|
||||||
ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
|
|
||||||
mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.path.APP_DIR_FILE_SEC);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyActionMode(ActionMode mode) {
|
|
||||||
mAdapter.clearSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
|
|
||||||
boolean checked) {
|
|
||||||
if (checked) {
|
|
||||||
mAdapter.setNewSelection(position, checked);
|
|
||||||
} else {
|
|
||||||
mAdapter.removeSelection(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = getListView().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.
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
|
|
||||||
// Start out with a progress indicator.
|
|
||||||
setListShown(false);
|
|
||||||
|
|
||||||
// Create an empty adapter we will use to display the loaded data.
|
|
||||||
mAdapter = new KeyListSecretAdapter(getActivity(), null, 0);
|
|
||||||
setListAdapter(mAdapter);
|
|
||||||
|
|
||||||
// Prepare the loader. Either re-connect with an existing one,
|
|
||||||
// or start a new one.
|
|
||||||
getLoaderManager().initLoader(0, null, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are the rows that we will retrieve.
|
|
||||||
static final String[] PROJECTION = new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID,
|
|
||||||
UserIds.USER_ID};
|
|
||||||
static final String SORT_ORDER = UserIds.USER_ID + " COLLATE LOCALIZED ASC";
|
|
||||||
|
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
|
||||||
// This is called when a new Loader needs to be created. This
|
|
||||||
// sample only has one Loader, so we don't care about the ID.
|
|
||||||
// First, pick the base URI to use depending on whether we are
|
|
||||||
// currently filtering.
|
|
||||||
Uri baseUri = KeyRings.buildSecretKeyRingsUri();
|
|
||||||
|
|
||||||
// Now create and return a CursorLoader that will take care of
|
|
||||||
// creating a Cursor for the data being displayed.
|
|
||||||
return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
|
||||||
// Swap the new cursor in. (The framework will take care of closing the
|
|
||||||
// old cursor once we return.)
|
|
||||||
mAdapter.swapCursor(data);
|
|
||||||
|
|
||||||
// The list should now be shown.
|
|
||||||
if (isResumed()) {
|
|
||||||
setListShown(true);
|
|
||||||
} else {
|
|
||||||
setListShownNoAnimation(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onLoaderReset(Loader<Cursor> loader) {
|
|
||||||
// This is called when the last Cursor provided to onLoadFinished()
|
|
||||||
// above is about to be closed. We need to make sure we are no
|
|
||||||
// longer using it.
|
|
||||||
mAdapter.swapCursor(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On click on item, start key view activity
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
|
||||||
Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
|
|
||||||
editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(id)));
|
|
||||||
editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
|
|
||||||
startActivityForResult(editIntent, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show dialog to delete key
|
|
||||||
*
|
|
||||||
* @param keyRingRowIds
|
|
||||||
*/
|
|
||||||
@TargetApi(11)
|
|
||||||
public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
|
|
||||||
// Message is received after key is deleted
|
|
||||||
Handler returnHandler = new Handler() {
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message message) {
|
|
||||||
if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
|
|
||||||
mode.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
|
||||||
Messenger messenger = new Messenger(returnHandler);
|
|
||||||
|
|
||||||
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
|
|
||||||
keyRingRowIds, Id.type.secret_key);
|
|
||||||
|
|
||||||
deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.R;
|
|||||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||||
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
|
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||||
@ -83,7 +84,13 @@ public class ViewKeyActivity extends ActionBarActivity {
|
|||||||
selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
|
selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
|
||||||
}
|
}
|
||||||
|
|
||||||
mDataUri = getIntent().getData();
|
{
|
||||||
|
// normalize mDataUri to a "by row id" query, to ensure it works with any
|
||||||
|
// given valid /public/ query
|
||||||
|
long rowId = ProviderHelper.getRowId(this, getIntent().getData());
|
||||||
|
// TODO: handle (rowId == 0) with something else than a crash
|
||||||
|
mDataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)) ;
|
||||||
|
}
|
||||||
|
|
||||||
Bundle mainBundle = new Bundle();
|
Bundle mainBundle = new Bundle();
|
||||||
mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
|
mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
|
||||||
@ -107,7 +114,7 @@ public class ViewKeyActivity extends ActionBarActivity {
|
|||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case android.R.id.home:
|
case android.R.id.home:
|
||||||
Intent homeIntent = new Intent(this, KeyListPublicActivity.class);
|
Intent homeIntent = new Intent(this, KeyListActivity.class);
|
||||||
homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
startActivity(homeIntent);
|
startActivity(homeIntent);
|
||||||
return true;
|
return true;
|
||||||
|
@ -67,6 +67,8 @@ public class ViewKeyMainFragment extends Fragment implements
|
|||||||
private TextView mExpiry;
|
private TextView mExpiry;
|
||||||
private TextView mCreation;
|
private TextView mCreation;
|
||||||
private TextView mFingerprint;
|
private TextView mFingerprint;
|
||||||
|
private TextView mSecretKey;
|
||||||
|
private BootstrapButton mActionEdit;
|
||||||
private BootstrapButton mActionEncrypt;
|
private BootstrapButton mActionEncrypt;
|
||||||
|
|
||||||
private ListView mUserIds;
|
private ListView mUserIds;
|
||||||
@ -93,8 +95,10 @@ public class ViewKeyMainFragment extends Fragment implements
|
|||||||
mCreation = (TextView) view.findViewById(R.id.creation);
|
mCreation = (TextView) view.findViewById(R.id.creation);
|
||||||
mExpiry = (TextView) view.findViewById(R.id.expiry);
|
mExpiry = (TextView) view.findViewById(R.id.expiry);
|
||||||
mFingerprint = (TextView) view.findViewById(R.id.fingerprint);
|
mFingerprint = (TextView) view.findViewById(R.id.fingerprint);
|
||||||
|
mSecretKey = (TextView) view.findViewById(R.id.secret_key);
|
||||||
mUserIds = (ListView) view.findViewById(R.id.user_ids);
|
mUserIds = (ListView) view.findViewById(R.id.user_ids);
|
||||||
mKeys = (ListView) view.findViewById(R.id.keys);
|
mKeys = (ListView) view.findViewById(R.id.keys);
|
||||||
|
mActionEdit = (BootstrapButton) view.findViewById(R.id.action_edit);
|
||||||
mActionEncrypt = (BootstrapButton) view.findViewById(R.id.action_encrypt);
|
mActionEncrypt = (BootstrapButton) view.findViewById(R.id.action_encrypt);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
@ -124,6 +128,31 @@ public class ViewKeyMainFragment extends Fragment implements
|
|||||||
|
|
||||||
Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
|
Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
|
||||||
|
|
||||||
|
{ // label whether secret key is available, and edit button if it is
|
||||||
|
final long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), mDataUri);
|
||||||
|
if(ProviderHelper.hasSecretKeyByMasterKeyId(getActivity(), masterKeyId)) {
|
||||||
|
// set this attribute. this is a LITTLE unclean, but we have the info available
|
||||||
|
// right here, so why not.
|
||||||
|
mSecretKey.setTextColor(getResources().getColor(R.color.emphasis));
|
||||||
|
mSecretKey.setText(R.string.secret_key_yes);
|
||||||
|
|
||||||
|
// edit button
|
||||||
|
mActionEdit.setVisibility(View.VISIBLE);
|
||||||
|
mActionEdit.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
|
||||||
|
editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)));
|
||||||
|
editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
|
||||||
|
startActivityForResult(editIntent, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mSecretKey.setTextColor(Color.BLACK);
|
||||||
|
mSecretKey.setText(getResources().getString(R.string.secret_key_no));
|
||||||
|
mActionEdit.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mActionEncrypt.setOnClickListener(new View.OnClickListener() {
|
mActionEncrypt.setOnClickListener(new View.OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,221 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui.adapter;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements StickyListHeadersAdapter from library
|
|
||||||
*/
|
|
||||||
public class KeyListPublicAdapter extends HighlightQueryCursorAdapter implements StickyListHeadersAdapter {
|
|
||||||
private LayoutInflater mInflater;
|
|
||||||
private int mSectionColumnIndex;
|
|
||||||
private int mIndexUserId;
|
|
||||||
private int mIndexIsRevoked;
|
|
||||||
|
|
||||||
@SuppressLint("UseSparseArrays")
|
|
||||||
private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
|
|
||||||
|
|
||||||
public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) {
|
|
||||||
super(context, c, flags);
|
|
||||||
mInflater = LayoutInflater.from(context);
|
|
||||||
mSectionColumnIndex = sectionColumnIndex;
|
|
||||||
initIndex(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor swapCursor(Cursor newCursor) {
|
|
||||||
initIndex(newCursor);
|
|
||||||
|
|
||||||
return super.swapCursor(newCursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get column indexes for performance reasons just once in constructor and swapCursor. For a
|
|
||||||
* performance comparison see http://stackoverflow.com/a/17999582
|
|
||||||
*
|
|
||||||
* @param cursor
|
|
||||||
*/
|
|
||||||
private void initIndex(Cursor cursor) {
|
|
||||||
if (cursor != null) {
|
|
||||||
mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID);
|
|
||||||
mIndexIsRevoked = cursor.getColumnIndexOrThrow(KeychainContract.Keys.IS_REVOKED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind cursor data to the item list view
|
|
||||||
* <p/>
|
|
||||||
* NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus
|
|
||||||
* no ViewHolder is required here.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
|
|
||||||
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
|
||||||
TextView revoked = (TextView) view.findViewById(R.id.revoked);
|
|
||||||
|
|
||||||
String userId = cursor.getString(mIndexUserId);
|
|
||||||
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
|
||||||
if (userIdSplit[0] != null) {
|
|
||||||
mainUserId.setText(highlightSearchQuery(userIdSplit[0]));
|
|
||||||
} else {
|
|
||||||
mainUserId.setText(R.string.user_id_no_name);
|
|
||||||
}
|
|
||||||
if (userIdSplit[1] != null) {
|
|
||||||
mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1]));
|
|
||||||
mainUserIdRest.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
mainUserIdRest.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isRevoked = cursor.getInt(mIndexIsRevoked) > 0;
|
|
||||||
if (isRevoked) {
|
|
||||||
revoked.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
revoked.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
|
||||||
return mInflater.inflate(R.layout.key_list_public_item, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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_public_header, parent, false);
|
|
||||||
holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text);
|
|
||||||
convertView.setTag(holder);
|
|
||||||
} else {
|
|
||||||
holder = (HeaderViewHolder) convertView.getTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mDataValid) {
|
|
||||||
// no data available at this point
|
|
||||||
Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mCursor.moveToPosition(position)) {
|
|
||||||
throw new IllegalStateException("couldn't move cursor to position " + position);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set header text as first char in user id
|
|
||||||
String userId = mCursor.getString(mSectionColumnIndex);
|
|
||||||
String headerText = convertView.getResources().getString(R.string.user_id_no_name);
|
|
||||||
if (userId != null && userId.length() > 0) {
|
|
||||||
headerText = "" + mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
|
|
||||||
}
|
|
||||||
holder.text.setText(headerText);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the first character of the name as ID because this is what
|
|
||||||
// headers are based upon
|
|
||||||
String userId = mCursor.getString(mSectionColumnIndex);
|
|
||||||
if (userId != null && userId.length() > 0) {
|
|
||||||
return userId.subSequence(0, 1).charAt(0);
|
|
||||||
} else {
|
|
||||||
return Long.MAX_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HeaderViewHolder {
|
|
||||||
TextView text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* -------------------------- MULTI-SELECTION METHODS --------------
|
|
||||||
*/
|
|
||||||
public void setNewSelection(int position, boolean value) {
|
|
||||||
mSelection.put(position, value);
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeSelection(int position) {
|
|
||||||
mSelection.remove(position);
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearSelection() {
|
|
||||||
mSelection.clear();
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change color for multi-selection
|
|
||||||
*/
|
|
||||||
if (mSelection.get(position) != null && mSelection.get(position).booleanValue()) {
|
|
||||||
// color for selected items
|
|
||||||
v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
|
|
||||||
} else {
|
|
||||||
// default color
|
|
||||||
v.setBackgroundColor(Color.TRANSPARENT);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui.adapter;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.support.v4.widget.CursorAdapter;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
public class KeyListSecretAdapter extends CursorAdapter {
|
|
||||||
private LayoutInflater mInflater;
|
|
||||||
|
|
||||||
private int mIndexUserId;
|
|
||||||
|
|
||||||
@SuppressLint("UseSparseArrays")
|
|
||||||
private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
|
|
||||||
|
|
||||||
public KeyListSecretAdapter(Context context, Cursor c, int flags) {
|
|
||||||
super(context, c, flags);
|
|
||||||
|
|
||||||
mInflater = LayoutInflater.from(context);
|
|
||||||
initIndex(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor swapCursor(Cursor newCursor) {
|
|
||||||
initIndex(newCursor);
|
|
||||||
|
|
||||||
return super.swapCursor(newCursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get column indexes for performance reasons just once in constructor and swapCursor. For a
|
|
||||||
* performance comparison see http://stackoverflow.com/a/17999582
|
|
||||||
*
|
|
||||||
* @param cursor
|
|
||||||
*/
|
|
||||||
private void initIndex(Cursor cursor) {
|
|
||||||
if (cursor != null) {
|
|
||||||
mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
|
|
||||||
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
|
||||||
|
|
||||||
String userId = cursor.getString(mIndexUserId);
|
|
||||||
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
|
||||||
|
|
||||||
if (userIdSplit[0] != null) {
|
|
||||||
mainUserId.setText(userIdSplit[0]);
|
|
||||||
} else {
|
|
||||||
mainUserId.setText(R.string.user_id_no_name);
|
|
||||||
}
|
|
||||||
if (userIdSplit[1] != null) {
|
|
||||||
mainUserIdRest.setText(userIdSplit[1]);
|
|
||||||
} else {
|
|
||||||
mainUserIdRest.setText("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
|
||||||
return mInflater.inflate(R.layout.key_list_secret_item, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** -------------------------- MULTI-SELECTION METHODS -------------- */
|
|
||||||
public void setNewSelection(int position, boolean value) {
|
|
||||||
mSelection.put(position, value);
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeSelection(int position) {
|
|
||||||
mSelection.remove(position);
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearSelection() {
|
|
||||||
mSelection.clear();
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change color for multi-selection
|
|
||||||
*/
|
|
||||||
if (mSelection.get(position) != null) {
|
|
||||||
// color for selected items
|
|
||||||
v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
|
|
||||||
} else {
|
|
||||||
// default color
|
|
||||||
v.setBackgroundColor(Color.TRANSPARENT);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -9,8 +9,8 @@
|
|||||||
android:layout_height="match_parent" >
|
android:layout_height="match_parent" >
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/key_list_public_fragment"
|
android:id="@+id/key_list_fragment"
|
||||||
android:name="org.sufficientlysecure.keychain.ui.KeyListPublicFragment"
|
android:name="org.sufficientlysecure.keychain.ui.KeyListFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<!--rebuild functionality of ListFragment -->
|
<!--rebuild functionality of ListFragment -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/key_list_public_progress_container"
|
android:id="@+id/key_list_progress_container"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -30,12 +30,12 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/key_list_public_list_container"
|
android:id="@+id/key_list_list_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<se.emilsjolander.stickylistheaders.StickyListHeadersListView
|
<se.emilsjolander.stickylistheaders.StickyListHeadersListView
|
||||||
android:id="@+id/key_list_public_list"
|
android:id="@+id/key_list_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
@ -47,7 +47,7 @@
|
|||||||
android:scrollbarStyle="outsideOverlay" />
|
android:scrollbarStyle="outsideOverlay" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/key_list_public_empty"
|
android:id="@+id/key_list_empty"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
30
OpenPGP-Keychain/src/main/res/layout/key_list_header.xml
Normal file
30
OpenPGP-Keychain/src/main/res/layout/key_list_header.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?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" >
|
||||||
|
|
||||||
|
<org.sufficientlysecure.keychain.ui.widget.UnderlineTextView
|
||||||
|
android:id="@+id/stickylist_header_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start|left"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textColor="@color/emphasis"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="header text" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:text="contact count"
|
||||||
|
android:id="@+id/contacts_num"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_marginRight="10px"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:textColor="@android:color/darker_gray" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -3,13 +3,13 @@
|
|||||||
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="?android:attr/listPreferredItemHeight"
|
||||||
android:layout_marginRight="?android:attr/scrollbarSize"
|
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp"
|
|
||||||
android:paddingTop="4dp"
|
android:paddingTop="4dp"
|
||||||
android:paddingBottom="4dp"
|
android:paddingBottom="4dp"
|
||||||
android:singleLine="true">
|
android:singleLine="true"
|
||||||
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
android:focusable="false">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/mainUserId"
|
android:id="@+id/mainUserId"
|
||||||
@ -17,6 +17,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/label_main_user_id"
|
android:text="@string/label_main_user_id"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true" />
|
android:layout_alignParentStart="true" />
|
||||||
|
|
||||||
@ -30,6 +31,23 @@
|
|||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true" />
|
android:layout_alignParentStart="true" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@android:style/Widget.DeviceDefault.Button.Borderless.Small"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/edit"
|
||||||
|
android:focusable="false"
|
||||||
|
android:layout_alignTop="@+id/mainUserId"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentBottom="false"
|
||||||
|
android:layout_alignParentTop="false"
|
||||||
|
android:layout_alignBottom="@+id/mainUserIdRest"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:enabled="true"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:text="@string/edit" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/revoked"
|
android:id="@+id/revoked"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -37,7 +55,7 @@
|
|||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:text="@string/revoked"
|
android:text="@string/revoked"
|
||||||
android:textColor="#e00"
|
android:textColor="#e00"
|
||||||
android:visibility="gone"
|
android:visibility="visible"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_alignParentEnd="true" />
|
android:layout_alignParentEnd="true" />
|
@ -1,16 +0,0 @@
|
|||||||
<?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" >
|
|
||||||
|
|
||||||
<org.sufficientlysecure.keychain.ui.widget.UnderlineTextView
|
|
||||||
android:id="@+id/stickylist_header_text"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="start|left"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:textColor="@color/emphasis"
|
|
||||||
android:textSize="17sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/drawer_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/key_list_secret_fragment"
|
|
||||||
android:name="org.sufficientlysecure.keychain.ui.KeyListSecretFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
android:paddingLeft="16dp"
|
|
||||||
android:paddingRight="16dp"
|
|
||||||
android:scrollbarStyle="outsideOverlay" />
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<include layout="@layout/drawer_list" />
|
|
||||||
|
|
||||||
</android.support.v4.widget.DrawerLayout>
|
|
@ -1,32 +0,0 @@
|
|||||||
<?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"
|
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:paddingTop="4dp"
|
|
||||||
android:paddingBottom="4dp"
|
|
||||||
android:singleLine="true">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/mainUserId"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/label_main_user_id"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:layout_alignParentTop="true"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentStart="true" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/mainUserIdRest"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="<user@example.com>"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:layout_below="@+id/mainUserId"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentStart="true" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
@ -160,7 +160,10 @@
|
|||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
<TableRow>
|
<TableRow
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:id="@+id/tableRow">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -175,6 +178,22 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:typeface="monospace" />
|
android:typeface="monospace" />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:paddingRight="10dip"
|
||||||
|
android:text="@string/label_secret_key" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/secret_key"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:typeface="monospace" />
|
||||||
|
</TableRow>
|
||||||
</TableLayout>
|
</TableLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -211,6 +230,17 @@
|
|||||||
android:layout_marginTop="14dp"
|
android:layout_marginTop="14dp"
|
||||||
android:text="@string/section_actions" />
|
android:text="@string/section_actions" />
|
||||||
|
|
||||||
|
<com.beardedhen.androidbootstrap.BootstrapButton
|
||||||
|
android:id="@+id/action_edit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:text="@string/key_view_action_edit"
|
||||||
|
bootstrapbutton:bb_icon_left="fa-key"
|
||||||
|
bootstrapbutton:bb_type="info"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<com.beardedhen.androidbootstrap.BootstrapButton
|
<com.beardedhen.androidbootstrap.BootstrapButton
|
||||||
android:id="@+id/action_encrypt"
|
android:id="@+id/action_encrypt"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_key_list_public_import"
|
android:id="@+id/menu_key_list_import"
|
||||||
app:showAsAction="ifRoom|withText"
|
app:showAsAction="ifRoom|withText"
|
||||||
android:icon="@drawable/ic_action_add_person"
|
android:icon="@drawable/ic_action_add_person"
|
||||||
android:title="@string/menu_import" />
|
android:title="@string/menu_import" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_key_list_public_export"
|
android:id="@+id/menu_key_list_export"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
android:title="@string/menu_export_keys" />
|
android:title="@string/menu_export_keys" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_key_list_public_search"
|
android:id="@+id/menu_key_list_search"
|
||||||
android:title="@string/menu_search"
|
android:title="@string/menu_search"
|
||||||
android:icon="@drawable/ic_action_search"
|
android:icon="@drawable/ic_action_search"
|
||||||
app:actionViewClass="android.support.v7.widget.SearchView"
|
app:actionViewClass="android.support.v7.widget.SearchView"
|
@ -2,19 +2,19 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_key_list_public_multi_select_all"
|
android:id="@+id/menu_key_list_multi_select_all"
|
||||||
android:icon="@drawable/ic_action_select_all"
|
android:icon="@drawable/ic_action_select_all"
|
||||||
android:title="@string/menu_select_all" />
|
android:title="@string/menu_select_all" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_key_list_public_multi_export"
|
android:id="@+id/menu_key_list_multi_export"
|
||||||
android:icon="@drawable/ic_action_import_export"
|
android:icon="@drawable/ic_action_import_export"
|
||||||
android:title="@string/menu_export_key" />
|
android:title="@string/menu_export_key" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_key_list_public_multi_encrypt"
|
android:id="@+id/menu_key_list_multi_encrypt"
|
||||||
android:icon="@drawable/ic_action_secure"
|
android:icon="@drawable/ic_action_secure"
|
||||||
android:title="@string/menu_encrypt_to" />
|
android:title="@string/menu_encrypt_to" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_key_list_public_multi_delete"
|
android:id="@+id/menu_key_list_multi_delete"
|
||||||
android:icon="@drawable/ic_action_discard"
|
android:icon="@drawable/ic_action_discard"
|
||||||
android:title="@string/menu_delete_key" />
|
android:title="@string/menu_delete_key" />
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_key_list_secret_create"
|
|
||||||
app:showAsAction="ifRoom|withText"
|
|
||||||
android:title="@string/menu_create_key" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_key_list_secret_create_expert"
|
|
||||||
app:showAsAction="never"
|
|
||||||
android:title="@string/menu_create_key_expert" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_key_list_secret_import"
|
|
||||||
app:showAsAction="never"
|
|
||||||
android:title="@string/menu_import" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_key_list_secret_export"
|
|
||||||
app:showAsAction="never"
|
|
||||||
android:title="@string/menu_export_keys" />
|
|
||||||
|
|
||||||
</menu>
|
|
@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_key_list_public_multi_select_all"
|
|
||||||
android:icon="@drawable/ic_action_select_all"
|
|
||||||
android:title="@string/menu_select_all" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_key_list_public_multi_export"
|
|
||||||
android:icon="@drawable/ic_action_import_export"
|
|
||||||
android:title="@string/menu_export_key" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_key_list_public_multi_delete"
|
|
||||||
android:icon="@drawable/ic_action_discard"
|
|
||||||
android:title="@string/menu_delete_key" />
|
|
||||||
|
|
||||||
</menu>
|
|
@ -155,6 +155,11 @@
|
|||||||
<string name="revoked">revoked</string>
|
<string name="revoked">revoked</string>
|
||||||
<string name="user_id">User ID</string>
|
<string name="user_id">User ID</string>
|
||||||
|
|
||||||
|
<plurals name="n_contacts">
|
||||||
|
<item quantity="one">1 contact</item>
|
||||||
|
<item quantity="other">%d contacts</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
<plurals name="n_key_servers">
|
<plurals name="n_key_servers">
|
||||||
<item quantity="one">%d keyserver</item>
|
<item quantity="one">%d keyserver</item>
|
||||||
<item quantity="other">%d keyservers</item>
|
<item quantity="other">%d keyservers</item>
|
||||||
@ -438,6 +443,7 @@
|
|||||||
<string name="key_list_empty_button_import">importing keys.</string>
|
<string name="key_list_empty_button_import">importing keys.</string>
|
||||||
|
|
||||||
<!-- Key view -->
|
<!-- Key view -->
|
||||||
|
<string name="key_view_action_edit">Edit this key</string>
|
||||||
<string name="key_view_action_encrypt">Encrypt to this contact</string>
|
<string name="key_view_action_encrypt">Encrypt to this contact</string>
|
||||||
<string name="key_view_action_certify">Certify this contact\'s key</string>
|
<string name="key_view_action_certify">Certify this contact\'s key</string>
|
||||||
<string name="key_view_tab_main">Info</string>
|
<string name="key_view_tab_main">Info</string>
|
||||||
@ -452,5 +458,10 @@
|
|||||||
<string name="nav_apps">Registered Apps</string>
|
<string name="nav_apps">Registered Apps</string>
|
||||||
<string name="drawer_open">Open navigation drawer</string>
|
<string name="drawer_open">Open navigation drawer</string>
|
||||||
<string name="drawer_close">Close navigation drawer</string>
|
<string name="drawer_close">Close navigation drawer</string>
|
||||||
|
<string name="edit">Edit</string>
|
||||||
|
<string name="my_keys">My Keys</string>
|
||||||
|
<string name="label_secret_key">Secret Key</string>
|
||||||
|
<string name="secret_key_yes">available</string>
|
||||||
|
<string name="secret_key_no">unavailable</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user