aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--OpenPGP-Keychain/src/main/AndroidManifest.xml18
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java40
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java30
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java5
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java6
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java)52
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java)316
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java70
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java232
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java11
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java29
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java221
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java129
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java52
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml317
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml13
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/file_dialog.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml (renamed from OpenPGP-Keychain/src/main/res/layout/key_list_public_activity.xml)4
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml (renamed from OpenPGP-Keychain/src/main/res/layout/key_list_public_fragment.xml)10
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_header.xml30
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_item.xml (renamed from OpenPGP-Keychain/src/main/res/layout/key_list_public_item.xml)28
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_public_header.xml16
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_secret_activity.xml24
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_secret_item.xml32
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml32
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/key_list.xml (renamed from OpenPGP-Keychain/src/main/res/menu/key_list_public.xml)32
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml (renamed from OpenPGP-Keychain/src/main/res/menu/key_list_public_multi.xml)10
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/key_list_secret.xml22
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/key_list_secret_multi.xml17
-rw-r--r--OpenPGP-Keychain/src/main/res/raw/help_faq.html12
-rw-r--r--OpenPGP-Keychain/src/main/res/values/strings.xml13
33 files changed, 783 insertions, 1067 deletions
diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml
index 4fabf7432..b1fbcf555 100644
--- a/OpenPGP-Keychain/src/main/AndroidManifest.xml
+++ b/OpenPGP-Keychain/src/main/AndroidManifest.xml
@@ -61,7 +61,7 @@
android:theme="@style/KeychainTheme"
android:label="@string/app_name">
<activity
- android:name=".ui.KeyListPublicActivity"
+ android:name=".ui.KeyListActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/app_name"
android:launchMode="singleTop">
@@ -72,12 +72,6 @@
</intent-filter>
</activity>
<activity
- android:name=".ui.KeyListSecretActivity"
- android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_manage_secret_keys"
- android:launchMode="singleTop">
- </activity>
- <activity
android:name=".ui.EditKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_edit_key"
@@ -86,19 +80,19 @@
android:name=".ui.ViewKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_key_details"
- android:parentActivityName=".ui.KeyListPublicActivity">
+ android:parentActivityName=".ui.KeyListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".ui.KeyListPublicActivity" />
+ android:value=".ui.KeyListActivity" />
</activity>
<activity
android:name=".ui.ViewKeyActivityJB"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_key_details"
- android:parentActivityName=".ui.KeyListPublicActivity">
+ android:parentActivityName=".ui.KeyListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".ui.KeyListPublicActivity" />
+ android:value=".ui.KeyListActivity" />
</activity>
<activity
android:name=".ui.SelectPublicKeyActivity"
@@ -438,4 +432,4 @@
<!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API" /> -->
</application>
-</manifest> \ No newline at end of file
+</manifest>
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index d8de30b37..d9356951e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -100,6 +100,10 @@ public class KeychainContract {
/** 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 Uri buildUnifiedKeyRingsUri() {
+ return CONTENT_URI;
+ }
+
public static Uri buildPublicKeyRingsUri() {
return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index 3b60ffc87..fdc4b7bda 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -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_PACKAGE_NAME = 303;
+ private static final int UNIFIED_KEY_RING = 401;
+
// private static final int DATA_STREAM = 401;
protected UriMatcher mUriMatcher;
@@ -95,6 +97,15 @@ public class KeychainProvider extends ContentProvider {
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
*
* <pre>
@@ -365,6 +376,10 @@ public class KeychainProvider extends ContentProvider {
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;
}
@@ -399,9 +414,11 @@ public class KeychainProvider extends ContentProvider {
* Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
*/
private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) {
- // public or secret keyring
- qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
+ if (match != UNIFIED_KEY_RING) {
+ // public or secret keyring
+ qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
+ qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
+ }
// join keyrings with keys and userIds
// 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);
+ // all query() parameters, for good measure
+ String groupBy = null, having = null;
+
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 SECRET_KEY_RING:
qb = buildKeyRingQuery(qb, match);
@@ -630,7 +662,7 @@ public class KeychainProvider extends ContentProvider {
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
c.setNotificationUri(getContext().getContentResolver(), uri);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index dd538fbf4..1c05344d6 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -483,13 +483,13 @@ public class ProviderHelper {
*/
public static boolean getSecretMasterKeyCanSign(Context context, long 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 static boolean getMasterKeyCanSign(Context context, Uri queryUri, long keyRingRowId) {
+ public static boolean getMasterKeyCanSign(Context context, Uri queryUri) {
String[] projection = new String[]{
KeyRings.MASTER_KEY_ID,
"(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS
@@ -515,6 +515,12 @@ public class ProviderHelper {
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
*/
@@ -546,6 +552,26 @@ public class ProviderHelper {
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
*/
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
index 08ca262c3..ebb520197 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -50,9 +50,9 @@ public class DrawerActivity extends ActionBarActivity {
private CharSequence mDrawerTitle;
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,
- KeyListSecretActivity.class, RegisteredAppsListActivity.class };
+ RegisteredAppsListActivity.class };
private Class mSelectedItem;
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-unlock", getString(R.string.nav_decrypt)),
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)) };
mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index 72dc97ccd..0c35bb2b1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -266,12 +266,10 @@ public class EditKeyActivity extends ActionBarActivity {
} else {
Log.d(Constants.TAG, "uri: " + mDataUri);
- long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
-
// 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);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
index ac8250bef..48068a6c4 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 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
@@ -17,8 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.ArrayList;
-
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
@@ -29,7 +27,7 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
public class HelpActivity extends ActionBarActivity {
- public static final String EXTRA_SELECTED_TAB = "selectedTab";
+ public static final String EXTRA_SELECTED_TAB = "selected_tab";
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
@@ -59,19 +57,24 @@ public class HelpActivity extends ActionBarActivity {
Bundle startBundle = new Bundle();
startBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_start);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_start)),
- HelpHtmlFragment.class, startBundle, (selectedTab == 0) );
+ HelpHtmlFragment.class, startBundle, (selectedTab == 0));
+
+ Bundle faqBundle = new Bundle();
+ faqBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_faq);
+ mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_faq)),
+ HelpHtmlFragment.class, faqBundle, (selectedTab == 1));
Bundle nfcBundle = new Bundle();
nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_nfc_beam);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
- HelpHtmlFragment.class, nfcBundle, (selectedTab == 1) );
+ HelpHtmlFragment.class, nfcBundle, (selectedTab == 2));
Bundle changelogBundle = new Bundle();
changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_changelog);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_changelog)),
- HelpHtmlFragment.class, changelogBundle, (selectedTab == 2) );
+ HelpHtmlFragment.class, changelogBundle, (selectedTab == 3));
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
- HelpAboutFragment.class, null, (selectedTab == 3) );
+ HelpAboutFragment.class, null, (selectedTab == 4));
}
} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index cd3c8b4fd..684ee6959 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 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
@@ -27,7 +27,7 @@ import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
-public class KeyListSecretActivity extends DrawerActivity {
+public class KeyListActivity extends DrawerActivity {
ExportHelper mExportHelper;
@@ -37,7 +37,7 @@ public class KeyListSecretActivity extends DrawerActivity {
mExportHelper = new ExportHelper(this);
- setContentView(R.layout.key_list_secret_activity);
+ setContentView(R.layout.key_list_activity);
// now setup navigation drawer in DrawerActivity...
setupDrawerNavigation(savedInstanceState);
@@ -46,33 +46,37 @@ public class KeyListSecretActivity extends DrawerActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.key_list_secret, menu);
+ getMenuInflater().inflate(R.menu.key_list, 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);
+ case R.id.menu_key_list_import:
+ Intent intentImport = new Intent(this, ImportKeysActivity.class);
+ startActivityForResult(intentImport, 0);
+
+ return true;
+ 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);
+
+ return true;
+ case R.id.menu_key_list_create:
+ createKey();
+
+ return true;
+ case R.id.menu_key_list_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);
+
+
+ default:
+ return super.onOptionsItemSelected(item);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index de965daa5..70ffe968d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2013-2014 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
@@ -17,26 +17,33 @@
package org.sufficientlysecure.keychain.ui;
+import java.util.HashMap;
import java.util.ArrayList;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
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.KeyTypes;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
+import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -62,7 +69,9 @@ import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
+import android.widget.Button;
import android.widget.ListView;
+import android.widget.TextView;
import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
@@ -71,10 +80,10 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
* 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> {
- private KeyListPublicAdapter mAdapter;
+ private KeyListAdapter mAdapter;
private StickyListHeadersListView mStickyList;
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
@@ -94,9 +103,9 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
*/
@Override
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);
@@ -125,8 +134,8 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
});
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
- mListContainer = root.findViewById(R.id.key_list_public_list_container);
- mProgressContainer = root.findViewById(R.id.key_list_public_progress_container);
+ mListContainer = root.findViewById(R.id.key_list_list_container);
+ mProgressContainer = root.findViewById(R.id.key_list_progress_container);
mListShown = true;
return root;
@@ -140,6 +149,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ mStickyList.setOnItemClickListener(this);
mStickyList.setAreHeadersSticky(true);
mStickyList.setDrawingListUnderStickyHeader(false);
mStickyList.setFastScrollEnabled(true);
@@ -149,7 +159,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
}
// 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
@@ -162,7 +172,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
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;
}
@@ -173,25 +183,30 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
@Override
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()) {
- case R.id.menu_key_list_public_multi_encrypt: {
+ case R.id.menu_key_list_multi_encrypt: {
+ ids = mAdapter.getCurrentSelectedMasterKeyIds();
encrypt(mode, ids);
break;
}
- case R.id.menu_key_list_public_multi_export: {
- ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
- mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
+ case R.id.menu_key_list_multi_delete: {
+ ids = mStickyList.getWrappedList().getCheckedItemIds();
+ showDeleteKeyDialog(mode, ids);
break;
}
- case R.id.menu_key_list_public_multi_delete: {
- showDeleteKeyDialog(mode, ids);
+ 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());
+ mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
break;
}
- case R.id.menu_key_list_public_multi_select_all: {
- //Select all
+ case R.id.menu_key_list_multi_select_all: {
+ // select all
for (int i = 0; i < mStickyList.getCount(); i++) {
mStickyList.setItemChecked(i, true);
}
@@ -231,7 +246,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
setListShown(false);
// 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);
// Prepare the loader. Either re-connect with an existing one,
@@ -242,20 +257,28 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[]{
KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.TYPE,
KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.UserIds.USER_ID,
KeychainContract.Keys.IS_REVOKED
};
- static final int USER_ID_INDEX = 2;
+ static final int INDEX_TYPE = 1;
+ static final int INDEX_MASTER_KEY_ID = 2;
+ static final int INDEX_USER_ID = 3;
+ static final int INDEX_IS_REVOKED = 4;
- 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
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.
- Uri baseUri = KeyRings.buildPublicKeyRingsUri();
+ Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
String where = null;
String whereArgs[] = null;
if (mCurQuery != null) {
@@ -273,6 +296,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
// old cursor once we return.)
mAdapter.setSearchQuery(mCurQuery);
mAdapter.swapCursor(data);
+
mStickyList.setAdapter(mAdapter);
// NOTE: Not supported by StickyListHeader, but reimplemented here
@@ -303,21 +327,15 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
} else {
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);
}
@TargetApi(11)
- public void encrypt(ActionMode mode, long[] keyRingRowIds) {
- // 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]);
- }
-
+ protected void encrypt(ActionMode mode, long[] keyRingMasterKeyIds) {
Intent intent = new Intent(getActivity(), EncryptActivity.class);
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
startActivityForResult(intent, 0);
@@ -330,6 +348,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
* @param keyRingRowIds
*/
@TargetApi(11)
+ // TODO: this method needs an overhaul to handle both public and secret keys gracefully!
public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@@ -368,7 +387,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
@Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
// 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);
// Execute this when searching
@@ -385,7 +404,7 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
public boolean onMenuItemActionCollapse(MenuItem item) {
mCurQuery = null;
mSearchView.setQuery("", true);
- getLoaderManager().restartLoader(0, null, KeyListPublicFragment.this);
+ getLoaderManager().restartLoader(0, null, KeyListFragment.this);
return true;
}
});
@@ -444,4 +463,229 @@ public class KeyListPublicFragment extends Fragment implements SearchView.OnQuer
public void setListShownNoAnimation(boolean shown) {
setListShown(shown, false);
}
+
+ /**
+ * Implements StickyListHeadersAdapter from library
+ */
+ private class KeyListAdapter extends HighlightQueryCursorAdapter implements StickyListHeadersAdapter {
+ private LayoutInflater mInflater;
+
+ 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);
+ }
+
+ @Override
+ public Cursor swapCursor(Cursor newCursor) {
+ return super.swapCursor(newCursor);
+ }
+
+ /**
+ * 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(INDEX_USER_ID);
+ 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(INDEX_MASTER_KEY_ID);
+ 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(INDEX_IS_REVOKED) > 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(INDEX_MASTER_KEY_ID);
+ }
+
+ @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_USER_ID);
+ String headerText = convertView.getResources().getString(R.string.user_id_no_name);
+ if (userId != null && userId.length() > 0) {
+ headerText = "" + mCursor.getString(KeyListFragment.INDEX_USER_ID).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_USER_ID);
+ 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
+ */
+ if (mSelection.get(position) != null) {
+ // selected position color
+ v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
+ } else {
+ // default color
+ v.setBackgroundColor(Color.TRANSPARENT);
+ }
+
+ return v;
+ }
+
+ }
+
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
deleted file mode 100644
index 9bd9ee225..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2012-2014 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 KeyListPublicActivity extends DrawerActivity {
-
- ExportHelper mExportHelper;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mExportHelper = new ExportHelper(this);
-
- setContentView(R.layout.key_list_public_activity);
-
- // now setup navigation drawer in DrawerActivity...
- setupDrawerNavigation(savedInstanceState);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.key_list_public, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_key_list_public_import:
- Intent intentImport = new Intent(this, ImportKeysActivity.class);
- startActivityForResult(intentImport, 0);
-
- return true;
- case R.id.menu_key_list_public_export:
- mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
-
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
deleted file mode 100644
index e84b2f4c8..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
+++ /dev/null
@@ -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");
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 4cc5e1b62..581d14a22 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
@@ -83,7 +84,13 @@ public class ViewKeyActivity extends ActionBarActivity {
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();
mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
@@ -107,7 +114,7 @@ public class ViewKeyActivity extends ActionBarActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
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);
startActivity(homeIntent);
return true;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
index a43e3f5b5..0e73ad983 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -67,6 +67,8 @@ public class ViewKeyMainFragment extends Fragment implements
private TextView mExpiry;
private TextView mCreation;
private TextView mFingerprint;
+ private TextView mSecretKey;
+ private BootstrapButton mActionEdit;
private BootstrapButton mActionEncrypt;
private ListView mUserIds;
@@ -93,8 +95,10 @@ public class ViewKeyMainFragment extends Fragment implements
mCreation = (TextView) view.findViewById(R.id.creation);
mExpiry = (TextView) view.findViewById(R.id.expiry);
mFingerprint = (TextView) view.findViewById(R.id.fingerprint);
+ mSecretKey = (TextView) view.findViewById(R.id.secret_key);
mUserIds = (ListView) view.findViewById(R.id.user_ids);
mKeys = (ListView) view.findViewById(R.id.keys);
+ mActionEdit = (BootstrapButton) view.findViewById(R.id.action_edit);
mActionEncrypt = (BootstrapButton) view.findViewById(R.id.action_encrypt);
return view;
@@ -124,6 +128,31 @@ public class ViewKeyMainFragment extends Fragment implements
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() {
@Override
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
deleted file mode 100644
index d7bb62d01..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ /dev/null
@@ -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;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
deleted file mode 100644
index 0bffcaa19..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
+++ /dev/null
@@ -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;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
index 32266839c..99cac1152 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
@@ -18,24 +18,7 @@
package org.sufficientlysecure.keychain.util;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.GregorianCalendar;
-import java.util.List;
-import java.util.TimeZone;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.Locale;
-
+import android.text.Html;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@@ -51,7 +34,14 @@ import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
-import android.text.Html;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.*;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* TODO:
@@ -82,7 +72,7 @@ public class HkpKeyServer extends KeyServer {
}
private String mHost;
- private short mPort = 11371;
+ private short mPort;
// example:
// pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a
@@ -95,9 +85,25 @@ public class HkpKeyServer extends KeyServer {
public static Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE
| Pattern.CASE_INSENSITIVE);
- public HkpKeyServer(String host) {
- mHost = host;
- }
+ private static final short PORT_DEFAULT = 11371;
+
+ /**
+ * @param hostAndPort may be just "<code>hostname</code>" (eg. "<code>pool.sks-keyservers.net</code>"), then it will
+ * connect using {@link #PORT_DEFAULT}. However, port may be specified after colon
+ * ("<code>hostname:port</code>", eg. "<code>p80.pool.sks-keyservers.net:80</code>").
+ */
+ public HkpKeyServer(String hostAndPort) {
+ String host = hostAndPort;
+ short port = PORT_DEFAULT;
+ final int colonPosition = hostAndPort.lastIndexOf(':');
+ if (colonPosition > 0) {
+ host = hostAndPort.substring(0, colonPosition);
+ final String portStr = hostAndPort.substring(colonPosition + 1);
+ port = Short.decode(portStr);
+ }
+ mHost = host;
+ mPort = port;
+ }
public HkpKeyServer(String host, short port) {
mHost = host;
diff --git a/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml
index a974f6ec0..25c7c000c 100644
--- a/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml
@@ -5,201 +5,206 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fillViewport="true"
- android:orientation="vertical">
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:orientation="vertical">
- <LinearLayout
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="4dp"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp">
+
+ <RelativeLayout
+ android:id="@+id/signature"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingTop="4dp"
+ android:clickable="true"
+ android:orientation="horizontal"
+ android:padding="4dp"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<RelativeLayout
- android:id="@+id/signature"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:clickable="true"
- android:orientation="horizontal"
- android:padding="4dp"
- android:paddingLeft="10dp"
- android:paddingRight="10dp">
+ android:id="@+id/relativeLayout">
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/relativeLayout">
-
- <ImageView
- android:id="@+id/ic_signature"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/signed_large" />
-
- <ImageView
- android:id="@+id/ic_signature_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/overlay_error" />
- </RelativeLayout>
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/lookup_key"
- android:visibility="gone"
- android:layout_width="wrap_content"
- android:layout_height="50dp"
- android:padding="4dp"
- android:text="@string/btn_lookup_key"
- bootstrapbutton:bb_icon_left="fa-download"
- bootstrapbutton:bb_type="info"
- bootstrapbutton:bb_size="small"
- android:layout_alignParentTop="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true" />
-
- <TextView
- android:id="@+id/mainUserId"
+ <ImageView
+ android:id="@+id/ic_signature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:text="@string/label_main_user_id"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_toRightOf="@+id/relativeLayout" />
+ android:src="@drawable/signed_large" />
- <TextView
- android:id="@+id/mainUserIdRest"
+ <ImageView
+ android:id="@+id/ic_signature_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:text="Main User Id Rest"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_below="@+id/mainUserId"
- android:layout_toRightOf="@+id/relativeLayout" />
+ android:src="@drawable/overlay_error" />
</RelativeLayout>
- <LinearLayout
- android:layout_width="match_parent"
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/lookup_key"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="50dp"
+ android:padding="4dp"
+ android:text="@string/btn_lookup_key"
+ bootstrapbutton:bb_icon_left="fa-download"
+ bootstrapbutton:bb_type="info"
+ bootstrapbutton:bb_size="small"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true" />
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:layout_gravity="left"
+ android:text="@string/label_main_user_id"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_toRightOf="@+id/relativeLayout" />
- <ImageView
- android:id="@+id/sourcePrevious"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_previous" />
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:text="Main User Id Rest"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_below="@+id/mainUserId"
+ android:layout_toRightOf="@+id/relativeLayout" />
+ </RelativeLayout>
- <TextView
- android:id="@+id/sourceLabel"
- style="@style/SectionHeader"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_horizontal|center_vertical"
- android:text="@string/label_message"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
- <ImageView
- android:id="@+id/sourceNext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_next" />
+ <ImageView
+ android:id="@+id/sourcePrevious"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_previous" />
+
+ <TextView
+ android:id="@+id/sourceLabel"
+ style="@style/SectionHeader"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_horizontal|center_vertical"
+ android:text="@string/label_message"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <ImageView
+ android:id="@+id/sourceNext"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_next" />
+ </LinearLayout>
+
+ <ViewFlipper
+ android:id="@+id/source"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:id="@+id/sourceMessage"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp">
+
+ <EditText
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="top"
+ android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
+ android:scrollHorizontally="true" />
</LinearLayout>
- <ViewFlipper
- android:id="@+id/source"
+ <LinearLayout
+ android:id="@+id/sourceFile"
android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp">
<LinearLayout
- android:id="@+id/sourceMessage"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="4dp">
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
<EditText
- android:id="@+id/message"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="top"
- android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
- android:scrollHorizontally="true" />
+ android:id="@+id/filename"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="top|left"
+ android:inputType="textMultiLine|textUri"
+ android:lines="4"
+ android:maxLines="10"
+ android:minLines="2"
+ android:scrollbars="vertical" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/btn_browse"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ bootstrapbutton:bb_icon_left="fa-folder-open"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
</LinearLayout>
<LinearLayout
- android:id="@+id/sourceFile"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="4dp">
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <EditText
- android:id="@+id/filename"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:inputType="textNoSuggestions" />
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/btn_browse"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="4dp"
- bootstrapbutton:bb_icon_left="fa-folder-open"
- bootstrapbutton:bb_roundedCorners="true"
- bootstrapbutton:bb_size="default"
- bootstrapbutton:bb_type="default" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
+ <CheckBox
+ android:id="@+id/deleteAfterDecryption"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <CheckBox
- android:id="@+id/deleteAfterDecryption"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_delete_after_decryption" />
- </LinearLayout>
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_delete_after_decryption" />
</LinearLayout>
- </ViewFlipper>
+ </LinearLayout>
+ </ViewFlipper>
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:text="@string/section_decrypt_verify" />
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:text="@string/section_decrypt_verify" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="4dp">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="4dp">
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/action_decrypt"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:text="@string/btn_decrypt_verify"
- bootstrapbutton:bb_icon_left="fa-unlock"
- bootstrapbutton:bb_type="info" />
- </LinearLayout>
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/action_decrypt"
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:padding="4dp"
+ android:text="@string/btn_decrypt_verify"
+ bootstrapbutton:bb_icon_left="fa-unlock"
+ bootstrapbutton:bb_type="info" />
</LinearLayout>
- </ScrollView>
+ </LinearLayout>
+ </ScrollView>
<include layout="@layout/drawer_list" />
diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml
index dc4cf0063..4fe65e341 100644
--- a/OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml
@@ -238,7 +238,12 @@
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:inputType="textNoSuggestions" />
+ android:gravity="top|left"
+ android:inputType="textMultiLine|textUri"
+ android:lines="4"
+ android:maxLines="10"
+ android:minLines="2"
+ android:scrollbars="vertical" />
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/btn_browse"
@@ -257,6 +262,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:clickable="true">
+
<com.beardedhen.androidbootstrap.FontAwesomeText
android:id="@+id/advancedSettingsIcon"
android:layout_width="wrap_content"
@@ -265,7 +271,8 @@
android:textSize="12sp"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
- fontawesometext:fa_icon="fa-chevron-right"/>
+ fontawesometext:fa_icon="fa-chevron-right" />
+
<TextView
android:id="@+id/advancedSettings"
android:layout_width="wrap_content"
@@ -273,7 +280,7 @@
android:text="@string/btn_encryption_advanced_settings_show"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
- android:textColor="@color/emphasis"/>
+ android:textColor="@color/emphasis" />
</LinearLayout>
<LinearLayout
diff --git a/OpenPGP-Keychain/src/main/res/layout/file_dialog.xml b/OpenPGP-Keychain/src/main/res/layout/file_dialog.xml
index a2939f571..83d697001 100644
--- a/OpenPGP-Keychain/src/main/res/layout/file_dialog.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/file_dialog.xml
@@ -26,8 +26,8 @@
android:layout_weight="1"
android:gravity="top|left"
android:inputType="textMultiLine|textUri"
- android:lines="2"
- android:maxLines="6"
+ android:lines="4"
+ android:maxLines="10"
android:minLines="2"
android:scrollbars="vertical" />
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_public_activity.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml
index f0e843e56..65d246d7b 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_public_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml
@@ -9,8 +9,8 @@
android:layout_height="match_parent" >
<fragment
- android:id="@+id/key_list_public_fragment"
- android:name="org.sufficientlysecure.keychain.ui.KeyListPublicFragment"
+ android:id="@+id/key_list_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.KeyListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_public_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml
index db82c8771..77bd6f4e9 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_public_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml
@@ -7,7 +7,7 @@
<!--rebuild functionality of ListFragment -->
<LinearLayout
- android:id="@+id/key_list_public_progress_container"
+ android:id="@+id/key_list_progress_container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -30,12 +30,12 @@
</LinearLayout>
<FrameLayout
- android:id="@+id/key_list_public_list_container"
+ android:id="@+id/key_list_list_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<se.emilsjolander.stickylistheaders.StickyListHeadersListView
- android:id="@+id/key_list_public_list"
+ android:id="@+id/key_list_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
@@ -47,7 +47,7 @@
android:scrollbarStyle="outsideOverlay" />
<LinearLayout
- android:id="@+id/key_list_public_empty"
+ android:id="@+id/key_list_empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
@@ -105,4 +105,4 @@
</FrameLayout>
-</FrameLayout> \ No newline at end of file
+</FrameLayout>
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_header.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_header.xml
new file mode 100644
index 000000000..09ac1c856
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_header.xml
@@ -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> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_public_item.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml
index f07d60214..f52693138 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_public_item.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml
@@ -3,13 +3,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:layout_marginRight="?android:attr/scrollbarSize"
android:gravity="center_vertical"
android:paddingLeft="8dp"
- android:paddingRight="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
- android:singleLine="true">
+ android:singleLine="true"
+ android:descendantFocusability="blocksDescendants"
+ android:focusable="false">
<TextView
android:id="@+id/mainUserId"
@@ -17,6 +17,7 @@
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" />
@@ -30,6 +31,23 @@
android:layout_alignParentLeft="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
android:id="@+id/revoked"
android:layout_width="wrap_content"
@@ -37,9 +55,9 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/revoked"
android:textColor="#e00"
- android:visibility="gone"
+ android:visibility="visible"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_public_header.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_public_header.xml
deleted file mode 100644
index 5768e4153..000000000
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_public_header.xml
+++ /dev/null
@@ -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> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_secret_activity.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_secret_activity.xml
deleted file mode 100644
index cd208a545..000000000
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_secret_activity.xml
+++ /dev/null
@@ -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> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_secret_item.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_secret_item.xml
deleted file mode 100644
index 7d5492265..000000000
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_secret_item.xml
+++ /dev/null
@@ -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="&lt;user@example.com>"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_below="@+id/mainUserId"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true" />
-
-</RelativeLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml
index b44ca82ec..3c8a4270b 100644
--- a/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml
@@ -160,7 +160,10 @@
android:layout_height="wrap_content" />
</TableRow>
- <TableRow>
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/tableRow">
<TextView
android:layout_width="wrap_content"
@@ -175,6 +178,22 @@
android:layout_height="wrap_content"
android:typeface="monospace" />
</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>
<TextView
@@ -212,6 +231,17 @@
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
android:id="@+id/action_encrypt"
android:layout_width="match_parent"
android:layout_height="60dp"
diff --git a/OpenPGP-Keychain/src/main/res/menu/key_list_public.xml b/OpenPGP-Keychain/src/main/res/menu/key_list.xml
index 35f4fca92..10223522c 100644
--- a/OpenPGP-Keychain/src/main/res/menu/key_list_public.xml
+++ b/OpenPGP-Keychain/src/main/res/menu/key_list.xml
@@ -3,18 +3,36 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
- android:id="@+id/menu_key_list_public_import"
+ android:id="@+id/menu_key_list_import"
app:showAsAction="ifRoom|withText"
android:icon="@drawable/ic_action_add_person"
android:title="@string/menu_import" />
+
<item
- android:id="@+id/menu_key_list_public_export"
- app:showAsAction="never"
- android:title="@string/menu_export_keys" />
- <item
- android:id="@+id/menu_key_list_public_search"
+ android:id="@+id/menu_key_list_search"
android:title="@string/menu_search"
android:icon="@drawable/ic_action_search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="collapseActionView|ifRoom" />
-</menu> \ No newline at end of file
+
+ <item
+ android:id="@+id/menu_key_list_create"
+ app:showAsAction="never"
+ android:title="@string/menu_create_key" />
+
+ <item
+ android:id="@+id/menu_key_list_create_expert"
+ app:showAsAction="never"
+ android:title="@string/menu_create_key_expert" />
+
+ <item
+ android:id="@+id/menu_key_list_export"
+ app:showAsAction="never"
+ android:title="@string/menu_export_keys" />
+
+ <item
+ android:id="@+id/menu_key_list_secret_export"
+ app:showAsAction="never"
+ android:title="@string/menu_export_secret_keys" />
+
+</menu>
diff --git a/OpenPGP-Keychain/src/main/res/menu/key_list_public_multi.xml b/OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml
index 9df17615e..db709052f 100644
--- a/OpenPGP-Keychain/src/main/res/menu/key_list_public_multi.xml
+++ b/OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml
@@ -2,20 +2,20 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<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:title="@string/menu_select_all" />
<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:title="@string/menu_export_key" />
<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:title="@string/menu_encrypt_to" />
<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:title="@string/menu_delete_key" />
-</menu> \ No newline at end of file
+</menu>
diff --git a/OpenPGP-Keychain/src/main/res/menu/key_list_secret.xml b/OpenPGP-Keychain/src/main/res/menu/key_list_secret.xml
deleted file mode 100644
index ce4999229..000000000
--- a/OpenPGP-Keychain/src/main/res/menu/key_list_secret.xml
+++ /dev/null
@@ -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> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/menu/key_list_secret_multi.xml b/OpenPGP-Keychain/src/main/res/menu/key_list_secret_multi.xml
deleted file mode 100644
index e7e46815e..000000000
--- a/OpenPGP-Keychain/src/main/res/menu/key_list_secret_multi.xml
+++ /dev/null
@@ -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> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/raw/help_faq.html b/OpenPGP-Keychain/src/main/res/raw/help_faq.html
new file mode 100644
index 000000000..b3d5b3a11
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw/help_faq.html
@@ -0,0 +1,12 @@
+<!-- Maintain structure with headings with h2 tags and content with p tags.
+This makes it easy to translate the values with transifex!
+And don't add newlines before or after p tags because of transifex -->
+<html>
+<head>
+</head>
+<body>
+<h2>TODO</h2>
+<p>text</p>
+
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml
index 07f402802..77891d6c7 100644
--- a/OpenPGP-Keychain/src/main/res/values/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values/strings.xml
@@ -78,6 +78,7 @@
<string name="menu_import">Import</string>
<string name="menu_import_from_nfc">Import from NFC</string>
<string name="menu_export_keys">Export all keys</string>
+ <string name="menu_export_secret_keys">Export all secret keys</string>
<string name="menu_export_key">Export to file</string>
<string name="menu_delete_key">Delete key</string>
<string name="menu_create_key">Create key</string>
@@ -155,6 +156,11 @@
<string name="revoked">revoked</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">
<item quantity="one">%d keyserver</item>
<item quantity="other">%d keyservers</item>
@@ -368,6 +374,7 @@
<!-- Help -->
<string name="help_tab_start">Start</string>
+ <string name="help_tab_faq">FAQ</string>
<string name="help_tab_nfc_beam">NFC Beam</string>
<string name="help_tab_changelog">Changelog</string>
<string name="help_tab_about">About</string>
@@ -438,6 +445,7 @@
<string name="key_list_empty_button_import">importing keys.</string>
<!-- 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_certify">Certify this contact\'s key</string>
<string name="key_view_tab_main">Info</string>
@@ -452,5 +460,10 @@
<string name="nav_apps">Registered Apps</string>
<string name="drawer_open">Open 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>