aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain/src/main/java/org
diff options
context:
space:
mode:
authorAshley Hughes <spirit.returned@gmail.com>2014-03-29 13:21:39 +0000
committerAshley Hughes <spirit.returned@gmail.com>2014-03-29 13:21:39 +0000
commite1d07214dfe12f893aa4052e08dbc3025f5cb67d (patch)
treeef945347e987806f4f448e32671c039345985356 /OpenPGP-Keychain/src/main/java/org
parent7a2d880c4db21ab62cb152f10d1938a36319acb3 (diff)
parent68668fd444b973502b91b7cbe0cf7dbdd8a8b5c4 (diff)
downloadopen-keychain-e1d07214dfe12f893aa4052e08dbc3025f5cb67d.tar.gz
open-keychain-e1d07214dfe12f893aa4052e08dbc3025f5cb67d.tar.bz2
open-keychain-e1d07214dfe12f893aa4052e08dbc3025f5cb67d.zip
merge master
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org')
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java26
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java5
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java62
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java41
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java102
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java37
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java55
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java38
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java199
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java37
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java50
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java138
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java246
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java226
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java256
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java129
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/NoAsymmetricEncryptionException.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java78
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java54
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java239
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java3
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java42
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java226
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java)61
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java54
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java)83
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java)107
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java27
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java)65
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java176
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java200
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java138
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java108
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java)7
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java)94
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java)91
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java232
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java86
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java57
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java243
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java76
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java10
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java163
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java65
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java175
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java112
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java72
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java13
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java31
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java13
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java94
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java9
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java12
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java66
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java6
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java84
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java14
-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)60
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java686
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java71
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java382
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java238
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java101
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java26
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java20
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java177
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java95
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java39
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java161
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java24
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java45
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java17
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java132
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java17
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java65
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java94
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java198
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java67
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java25
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java233
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java138
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java10
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java31
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java31
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java69
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java88
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java46
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java27
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java194
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java50
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java60
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java29
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java26
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java7
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java3
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FixedListView.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java203
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java37
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java126
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java68
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java7
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java219
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java12
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java10
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java16
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java18
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PRNGFixes.java51
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java50
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Primes.java252
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java12
123 files changed, 5829 insertions, 4249 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 4428c7133..f9a7962ec 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -16,10 +16,15 @@
package org.sufficientlysecure.keychain;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
-
import android.os.Environment;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.sufficientlysecure.keychain.remote.ui.AppsListActivity;
+import org.sufficientlysecure.keychain.ui.DecryptActivity;
+import org.sufficientlysecure.keychain.ui.EncryptActivity;
+import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
+import org.sufficientlysecure.keychain.ui.KeyListActivity;
+
public final class Constants {
public static final boolean DEBUG = BuildConfig.DEBUG;
@@ -40,12 +45,14 @@ public final class Constants {
public static final String INTENT_PREFIX = PACKAGE_NAME + ".action.";
- public static final class path {
+ public static final class Path {
public static final String APP_DIR = Environment.getExternalStorageDirectory()
+ "/OpenPGP-Keychain";
+ public static final String APP_DIR_FILE_SEC = APP_DIR + "/secexport.asc";
+ public static final String APP_DIR_FILE_PUB = APP_DIR + "/pubexport.asc";
}
- public static final class pref {
+ public static final class Pref {
public static final String DEFAULT_ENCRYPTION_ALGORITHM = "defaultEncryptionAlgorithm";
public static final String DEFAULT_HASH_ALGORITHM = "defaultHashAlgorithm";
public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour";
@@ -57,8 +64,17 @@ public final class Constants {
public static final String KEY_SERVERS = "keyServers";
}
- public static final class defaults {
+ public static final class Defaults {
public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu";
}
+ public static final class DrawerItems {
+ public static final Class KEY_LIST = KeyListActivity.class;
+ public static final Class ENCRYPT = EncryptActivity.class;
+ public static final Class DECRYPT = DecryptActivity.class;
+ public static final Class IMPORT_KEYS = ImportKeysActivity.class;
+ public static final Class REGISTERED_APPS_LIST = AppsListActivity.class;
+ public static final Class[] ARRAY = new Class[]{KEY_LIST, ENCRYPT, DECRYPT,
+ IMPORT_KEYS, REGISTERED_APPS_LIST};
+ }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
index 1d79edd43..784ec340e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
@@ -119,6 +119,7 @@ public final class Id {
public static final int secret_key = 0x21070002;
public static final int user_id = 0x21070003;
public static final int key = 0x21070004;
+ public static final int public_secret_key = 0x21070005;
}
public static final class choice {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index 4a25f2df1..98b827542 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -64,7 +64,7 @@ public class KeychainApplication extends Application {
// Create APG directory on sdcard if not existing
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- File dir = new File(Constants.path.APP_DIR);
+ File dir = new File(Constants.Path.APP_DIR);
if (!dir.exists() && !dir.mkdirs()) {
// ignore this for now, it's not crucial
// that the directory doesn't exist at this point
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
index 032af4c71..3164de7d1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
@@ -59,7 +59,6 @@ public class ClipboardReflection {
* Wrapper around ClipboardManager based on Android version using Reflection API
*
* @param context
- * @param text
*/
public static CharSequence getClipboardText(Context context) {
Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java
index a209f16cf..36f7fd23b 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java
@@ -37,8 +37,9 @@ import android.os.Handler;
* </code>
*/
public class DialogFragmentWorkaround {
- public static final SDKLevel17Interface INTERFACE = ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) ? new SDKLevel17Impl()
- : new SDKLevelPriorLevel17Impl());
+ public static final SDKLevel17Interface INTERFACE =
+ ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) ? new SDKLevel17Impl()
+ : new SDKLevelPriorLevel17Impl());
private static final int RUNNABLE_DELAY = 300;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
index b55075e6c..a26df556d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
@@ -17,28 +17,26 @@
package org.sufficientlysecure.keychain.helper;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.app.Activity;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.widget.TextView;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
public class ActionBarHelper {
/**
* Set actionbar without home button if called from another app
- *
+ *
* @param activity
*/
public static void setBackButton(ActionBarActivity activity) {
- // set actionbar without home button if called from another app
final ActionBar actionBar = activity.getSupportActionBar();
Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)="
+ activity.getCallingPackage());
@@ -54,30 +52,35 @@ public class ActionBarHelper {
/**
* Sets custom view on ActionBar for Done/Cancel activities
- *
+ *
* @param actionBar
- * @param doneText
- * @param doneOnClickListener
- * @param cancelText
- * @param cancelOnClickListener
+ * @param firstText
+ * @param firstDrawableId
+ * @param firstOnClickListener
+ * @param secondText
+ * @param secondDrawableId
+ * @param secondOnClickListener
*/
- public static void setDoneCancelView(ActionBar actionBar, int doneText,
- OnClickListener doneOnClickListener, int cancelText,
- OnClickListener cancelOnClickListener) {
+ public static void setTwoButtonView(ActionBar actionBar,
+ int firstText, int firstDrawableId, OnClickListener firstOnClickListener,
+ int secondText, int secondDrawableId, OnClickListener secondOnClickListener) {
- // Inflate a "Done"/"Cancel" custom action bar view
+ // Inflate the custom action bar view
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View customActionBarView = inflater.inflate(
R.layout.actionbar_custom_view_done_cancel, null);
- ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)).setText(doneText);
+ TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text));
+ firstTextView.setText(firstText);
+ firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0);
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
- doneOnClickListener);
- ((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text))
- .setText(cancelText);
+ firstOnClickListener);
+ TextView secondTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text));
+ secondTextView.setText(secondText);
+ secondTextView.setCompoundDrawablesWithIntrinsicBounds(secondDrawableId, 0, 0, 0);
customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
- cancelOnClickListener);
+ secondOnClickListener);
// Show the custom action bar view and hide the normal Home icon and title.
actionBar.setDisplayShowTitleEnabled(false);
@@ -89,22 +92,24 @@ public class ActionBarHelper {
/**
* Sets custom view on ActionBar for Done activities
- *
+ *
* @param actionBar
- * @param doneText
- * @param doneOnClickListener
+ * @param firstText
+ * @param firstOnClickListener
*/
- public static void setDoneView(ActionBar actionBar, int doneText,
- OnClickListener doneOnClickListener) {
+ public static void setOneButtonView(ActionBar actionBar, int firstText, int firstDrawableId,
+ OnClickListener firstOnClickListener) {
// Inflate a "Done" custom action bar view to serve as the "Up" affordance.
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View customActionBarView = inflater
.inflate(R.layout.actionbar_custom_view_done, null);
- ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)).setText(doneText);
+ TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text));
+ firstTextView.setText(firstText);
+ firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0);
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
- doneOnClickListener);
+ firstOnClickListener);
// Show the custom action bar view and hide the normal Home icon and title.
actionBar.setDisplayShowTitleEnabled(false);
@@ -112,5 +117,4 @@ public class ActionBarHelper {
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setCustomView(customActionBarView);
}
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java
new file mode 100644
index 000000000..f8ed21816
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.helper;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.Context;
+import android.util.Patterns;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ContactHelper {
+
+ public static final List<String> getMailAccounts(Context context) {
+ final Account[] accounts = AccountManager.get(context).getAccounts();
+ final Set<String> emailSet = new HashSet<String>();
+ for (Account account : accounts) {
+ if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
+ emailSet.add(account.name);
+ }
+ }
+ return new ArrayList<String>(emailSet);
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
index 21676a2a7..cc240a67b 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
@@ -16,10 +16,21 @@
package org.sufficientlysecure.keychain.helper;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v7.app.ActionBarActivity;
+import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@@ -27,44 +38,38 @@ import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.support.v7.app.ActionBarActivity;
-import android.widget.Toast;
+import java.lang.reflect.Array;
+import java.security.Provider;
+import java.util.ArrayList;
public class ExportHelper {
protected FileDialogFragment mFileDialog;
protected String mExportFilename;
- ActionBarActivity activity;
+ ActionBarActivity mActivity;
public ExportHelper(ActionBarActivity activity) {
super();
- this.activity = activity;
+ this.mActivity = activity;
}
- public void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) {
+ public void deleteKey(Uri dataUri, Handler deleteHandler) {
long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(deleteHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- new long[] { keyRingRowId }, keyType);
+ new long[]{keyRingRowId});
- deleteKeyDialog.show(activity.getSupportFragmentManager(), "deleteKeyDialog");
+ deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
}
/**
* Show dialog where to export keys
*/
- public void showExportKeysDialog(final Uri dataUri, final int keyType,
- final String exportFilename) {
+ public void showExportKeysDialog(final long[] masterKeyIds, final int keyType,
+ final String exportFilename, final String checkboxString) {
mExportFilename = exportFilename;
// Message is received after file is selected
@@ -73,9 +78,14 @@ public class ExportHelper {
public void handleMessage(Message message) {
if (message.what == FileDialogFragment.MESSAGE_OKAY) {
Bundle data = message.getData();
+ int type = keyType;
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
- exportKeys(dataUri, keyType);
+ if( data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED) ) {
+ type = Id.type.public_secret_key;
+ }
+
+ exportKeys(masterKeyIds, type);
}
}
};
@@ -86,25 +96,20 @@ public class ExportHelper {
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
String title = null;
- if (dataUri == null) {
+ if (masterKeyIds == null) {
// export all keys
- title = activity.getString(R.string.title_export_keys);
+ title = mActivity.getString(R.string.title_export_keys);
} else {
// export only key specified at data uri
- title = activity.getString(R.string.title_export_key);
+ title = mActivity.getString(R.string.title_export_key);
}
- String message = null;
- if (keyType == Id.type.public_key) {
- message = activity.getString(R.string.specify_file_to_export_to);
- } else {
- message = activity.getString(R.string.specify_file_to_export_secret_keys_to);
- }
+ String message = mActivity.getString(R.string.specify_file_to_export_to);
mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
- exportFilename, null);
+ exportFilename, checkboxString);
- mFileDialog.show(activity.getSupportFragmentManager(), "fileDialog");
+ mFileDialog.show(mActivity.getSupportFragmentManager(), "fileDialog");
}
});
}
@@ -112,11 +117,11 @@ public class ExportHelper {
/**
* Export keys
*/
- public void exportKeys(Uri dataUri, int keyType) {
+ public void exportKeys(long[] masterKeyIds, int keyType) {
Log.d(Constants.TAG, "exportKeys started");
// Send all information needed to service to export key in other thread
- Intent intent = new Intent(activity, KeychainIntentService.class);
+ final Intent intent = new Intent(mActivity, KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_EXPORT_KEYRING);
@@ -126,22 +131,27 @@ public class ExportHelper {
data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
- if (dataUri == null) {
+ if (masterKeyIds == null) {
data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
} else {
- // TODO: put data uri into service???
- long keyRingMasterKeyId = ProviderHelper.getMasterKeyId(activity, dataUri);
-
- data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId);
+ data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds);
}
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after exporting is done in ApgService
- KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(activity,
- R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
+ // Message is received after exporting is done in KeychainIntentService
+ KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(mActivity,
+ mActivity.getString(R.string.progress_exporting),
+ ProgressDialog.STYLE_HORIZONTAL,
+ true,
+ new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialogInterface) {
+ mActivity.stopService(intent);
+ }
+ }) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -151,16 +161,16 @@ public class ExportHelper {
int exported = returnData.getInt(KeychainIntentService.RESULT_EXPORT);
String toastMessage;
if (exported == 1) {
- toastMessage = activity.getString(R.string.key_exported);
+ toastMessage = mActivity.getString(R.string.key_exported);
} else if (exported > 0) {
- toastMessage = activity.getString(R.string.keys_exported, exported);
+ toastMessage = mActivity.getString(R.string.keys_exported, exported);
} else {
- toastMessage = activity.getString(R.string.no_keys_exported);
+ toastMessage = mActivity.getString(R.string.no_keys_exported);
}
- Toast.makeText(activity, toastMessage, Toast.LENGTH_SHORT).show();
+ Toast.makeText(mActivity, toastMessage, Toast.LENGTH_SHORT).show();
}
- };
+ }
};
// Create a new Messenger for the communication back
@@ -168,10 +178,10 @@ public class ExportHelper {
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
// show progress dialog
- exportHandler.showProgressDialog(activity);
+ exportHandler.showProgressDialog(mActivity);
// start service with intent
- activity.startService(intent);
+ mActivity.startService(intent);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
index ec56fe912..d24aeca52 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
@@ -17,10 +17,6 @@
package org.sufficientlysecure.keychain.helper;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -30,12 +26,15 @@ import android.net.Uri;
import android.os.Environment;
import android.support.v4.app.Fragment;
import android.widget.Toast;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
public class FileHelper {
/**
* Checks if external storage is mounted if file is located on external storage
- *
+ *
* @param file
* @return true if storage is mounted
*/
@@ -52,15 +51,12 @@ public class FileHelper {
/**
* Opens the preferred installed file manager on Android and shows a toast if no manager is
* installed.
- *
+ *
* @param activity
- * @param filename
- * default selected file, not supported by all file managers
- * @param mimeType
- * can be text/plain for example
- * @param requestCode
- * requestCode used to identify the result coming back from file manager to
- * onActivityResult() in your activity
+ * @param filename default selected file, not supported by all file managers
+ * @param mimeType can be text/plain for example
+ * @param requestCode requestCode used to identify the result coming back from file manager to
+ * onActivityResult() in your activity
*/
public static void openFile(Activity activity, String filename, String mimeType, int requestCode) {
Intent intent = buildFileIntent(filename, mimeType);
@@ -97,14 +93,13 @@ public class FileHelper {
/**
* Get a file path from a Uri.
- *
+ * <p/>
* from https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/
* afilechooser/utils/FileUtils.java
- *
+ *
* @param context
* @param uri
* @return
- *
* @author paulburke
*/
public static String getPath(Context context, Uri uri) {
@@ -115,21 +110,19 @@ public class FileHelper {
+ uri.getPathSegments().toString());
if ("content".equalsIgnoreCase(uri.getScheme())) {
- String[] projection = { "_data" };
+ String[] projection = {"_data"};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
- int column_index = cursor.getColumnIndexOrThrow("_data");
+ int columnIndex = cursor.getColumnIndexOrThrow("_data");
if (cursor.moveToFirst()) {
- return cursor.getString(column_index);
+ return cursor.getString(columnIndex);
}
} catch (Exception e) {
// Eat it
}
- }
-
- else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ } else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java
index 639ab17b8..b31a889f0 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java
@@ -17,21 +17,22 @@
package org.sufficientlysecure.keychain.helper;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.Iterator;
-import java.util.Set;
+import android.os.Bundle;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.StrikethroughSpan;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
-import android.os.Bundle;
+import java.util.Iterator;
+import java.util.Set;
public class OtherHelper {
/**
* Logs bundle content to debug for inspecting the content
- *
+ *
* @param bundle
* @param bundleName
*/
@@ -60,4 +61,10 @@ public class OtherHelper {
}
}
+ public static SpannableStringBuilder strikeOutText(CharSequence text) {
+ SpannableStringBuilder sb = new SpannableStringBuilder(text);
+ sb.setSpan(new StrikethroughSpan(), 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ return sb;
+ }
+
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
index 493f25707..515201b92 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
@@ -17,14 +17,13 @@
package org.sufficientlysecure.keychain.helper;
+import android.content.Context;
+import android.content.SharedPreferences;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
-import android.content.Context;
-import android.content.SharedPreferences;
-
import java.util.Vector;
/**
@@ -38,8 +37,8 @@ public class Preferences {
return getPreferences(context, false);
}
- public static synchronized Preferences getPreferences(Context context, boolean force_new) {
- if (mPreferences == null || force_new) {
+ public static synchronized Preferences getPreferences(Context context, boolean forceNew) {
+ if (mPreferences == null || forceNew) {
mPreferences = new Preferences(context);
}
return mPreferences;
@@ -50,17 +49,17 @@ public class Preferences {
}
public String getLanguage() {
- return mSharedPreferences.getString(Constants.pref.LANGUAGE, "");
+ return mSharedPreferences.getString(Constants.Pref.LANGUAGE, "");
}
public void setLanguage(String value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putString(Constants.pref.LANGUAGE, value);
+ editor.putString(Constants.Pref.LANGUAGE, value);
editor.commit();
}
public long getPassPhraseCacheTtl() {
- int ttl = mSharedPreferences.getInt(Constants.pref.PASS_PHRASE_CACHE_TTL, 180);
+ int ttl = mSharedPreferences.getInt(Constants.Pref.PASS_PHRASE_CACHE_TTL, 180);
// fix the value if it was set to "never" in previous versions, which currently is not
// supported
if (ttl == 0) {
@@ -71,81 +70,81 @@ public class Preferences {
public void setPassPhraseCacheTtl(int value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.PASS_PHRASE_CACHE_TTL, value);
+ editor.putInt(Constants.Pref.PASS_PHRASE_CACHE_TTL, value);
editor.commit();
}
public int getDefaultEncryptionAlgorithm() {
- return mSharedPreferences.getInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM,
+ return mSharedPreferences.getInt(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM,
PGPEncryptedData.AES_256);
}
public void setDefaultEncryptionAlgorithm(int value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM, value);
+ editor.putInt(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM, value);
editor.commit();
}
public int getDefaultHashAlgorithm() {
- return mSharedPreferences.getInt(Constants.pref.DEFAULT_HASH_ALGORITHM,
+ return mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM,
HashAlgorithmTags.SHA512);
}
public void setDefaultHashAlgorithm(int value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.DEFAULT_HASH_ALGORITHM, value);
+ editor.putInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, value);
editor.commit();
}
public int getDefaultMessageCompression() {
- return mSharedPreferences.getInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION,
+ return mSharedPreferences.getInt(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION,
Id.choice.compression.zlib);
}
public void setDefaultMessageCompression(int value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION, value);
+ editor.putInt(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION, value);
editor.commit();
}
public int getDefaultFileCompression() {
- return mSharedPreferences.getInt(Constants.pref.DEFAULT_FILE_COMPRESSION,
+ return mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION,
Id.choice.compression.none);
}
public void setDefaultFileCompression(int value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.DEFAULT_FILE_COMPRESSION, value);
+ editor.putInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, value);
editor.commit();
}
public boolean getDefaultAsciiArmour() {
- return mSharedPreferences.getBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, false);
+ return mSharedPreferences.getBoolean(Constants.Pref.DEFAULT_ASCII_ARMOUR, false);
}
public void setDefaultAsciiArmour(boolean value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, value);
+ editor.putBoolean(Constants.Pref.DEFAULT_ASCII_ARMOUR, value);
editor.commit();
}
public boolean getForceV3Signatures() {
- return mSharedPreferences.getBoolean(Constants.pref.FORCE_V3_SIGNATURES, false);
+ return mSharedPreferences.getBoolean(Constants.Pref.FORCE_V3_SIGNATURES, false);
}
public void setForceV3Signatures(boolean value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putBoolean(Constants.pref.FORCE_V3_SIGNATURES, value);
+ editor.putBoolean(Constants.Pref.FORCE_V3_SIGNATURES, value);
editor.commit();
}
public String[] getKeyServers() {
- String rawData = mSharedPreferences.getString(Constants.pref.KEY_SERVERS,
- Constants.defaults.KEY_SERVERS);
+ String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS,
+ Constants.Defaults.KEY_SERVERS);
Vector<String> servers = new Vector<String>();
String chunks[] = rawData.split(",");
- for (int i = 0; i < chunks.length; ++i) {
- String tmp = chunks[i].trim();
+ for (String c : chunks) {
+ String tmp = c.trim();
if (tmp.length() > 0) {
servers.add(tmp);
}
@@ -156,8 +155,8 @@ public class Preferences {
public void setKeyServers(String[] value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
String rawData = "";
- for (int i = 0; i < value.length; ++i) {
- String tmp = value[i].trim();
+ for (String v : value) {
+ String tmp = v.trim();
if (tmp.length() == 0) {
continue;
}
@@ -166,7 +165,7 @@ public class Preferences {
}
rawData += tmp;
}
- editor.putString(Constants.pref.KEY_SERVERS, rawData);
+ editor.putString(Constants.Pref.KEY_SERVERS, rawData);
editor.commit();
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
index c7dd7d647..1f8dec7a1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
@@ -17,11 +17,6 @@
package org.sufficientlysecure.keychain.pgp;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPSecretKey;
@@ -29,12 +24,17 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
public class PgpConversionHelper {
/**
* Convert from byte[] to PGPKeyRing
- *
+ *
* @param keysBytes
* @return
*/
@@ -54,7 +54,7 @@ public class PgpConversionHelper {
/**
* Convert from byte[] to ArrayList<PGPSecretKey>
- *
+ *
* @param keysBytes
* @return
*/
@@ -90,10 +90,10 @@ public class PgpConversionHelper {
/**
* Convert from byte[] to PGPSecretKey
- *
+ * <p/>
* Singles keys are encoded as keyRings with one single key in it by Bouncy Castle
- *
- * @param keysBytes
+ *
+ * @param keyBytes
* @return
*/
public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
@@ -105,13 +105,13 @@ public class PgpConversionHelper {
Log.e(Constants.TAG, "Error while converting to PGPSecretKey!", e);
}
PGPSecretKey secKey = null;
- if(obj instanceof PGPSecretKey) {
- if ((secKey = (PGPSecretKey)obj ) == null) {
+ if (obj instanceof PGPSecretKey) {
+ if ((secKey = (PGPSecretKey) obj) == null) {
Log.e(Constants.TAG, "No keys given!");
}
- } else if(obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
+ } else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
PGPSecretKeyRing keyRing = null;
- if ((keyRing = (PGPSecretKeyRing)obj) == null) {
+ if ((keyRing = (PGPSecretKeyRing) obj) == null) {
Log.e(Constants.TAG, "No keys given!");
}
secKey = keyRing.getSecretKey();
@@ -122,7 +122,7 @@ public class PgpConversionHelper {
/**
* Convert from ArrayList<PGPSecretKey> to byte[]
- *
+ *
* @param keys
* @return
*/
@@ -141,8 +141,8 @@ public class PgpConversionHelper {
/**
* Convert from PGPSecretKey to byte[]
- *
- * @param keysBytes
+ *
+ * @param key
* @return
*/
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
@@ -157,8 +157,8 @@ public class PgpConversionHelper {
/**
* Convert from PGPSecretKeyRing to byte[]
- *
- * @param keysBytes
+ *
+ * @param keyRing
* @return
*/
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index ccd6ff8df..571729bc5 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -18,38 +18,16 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
-
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.openpgp.PGPCompressedData;
-import org.spongycastle.openpgp.PGPEncryptedData;
-import org.spongycastle.openpgp.PGPEncryptedDataList;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPLiteralData;
-import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPOnePassSignature;
-import org.spongycastle.openpgp.PGPOnePassSignatureList;
-import org.spongycastle.openpgp.PGPPBEEncryptedData;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureList;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.*;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
+import org.spongycastle.openpgp.operator.jcajce.*;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
@@ -59,12 +37,7 @@ import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
import java.security.SignatureException;
import java.util.Iterator;
@@ -72,57 +45,57 @@ import java.util.Iterator;
* This class uses a Builder pattern!
*/
public class PgpDecryptVerify {
- private Context context;
- private InputData data;
- private OutputStream outStream;
+ private Context mContext;
+ private InputData mData;
+ private OutputStream mOutStream;
- private ProgressDialogUpdater progressDialogUpdater;
- private boolean assumeSymmetric;
- private String passphrase;
- private long enforcedKeyId;
+ private ProgressDialogUpdater mProgressDialogUpdater;
+ private boolean mAssumeSymmetric;
+ private String mPassphrase;
+ private long mEnforcedKeyId;
private PgpDecryptVerify(Builder builder) {
// private Constructor can only be called from Builder
- this.context = builder.context;
- this.data = builder.data;
- this.outStream = builder.outStream;
-
- this.progressDialogUpdater = builder.progressDialogUpdater;
- this.assumeSymmetric = builder.assumeSymmetric;
- this.passphrase = builder.passphrase;
- this.enforcedKeyId = builder.enforcedKeyId;
+ this.mContext = builder.mContext;
+ this.mData = builder.mData;
+ this.mOutStream = builder.mOutStream;
+
+ this.mProgressDialogUpdater = builder.mProgressDialogUpdater;
+ this.mAssumeSymmetric = builder.mAssumeSymmetric;
+ this.mPassphrase = builder.mPassphrase;
+ this.mEnforcedKeyId = builder.mEnforcedKeyId;
}
public static class Builder {
// mandatory parameter
- private Context context;
- private InputData data;
- private OutputStream outStream;
+ private Context mContext;
+ private InputData mData;
+ private OutputStream mOutStream;
// optional
- private ProgressDialogUpdater progressDialogUpdater = null;
- private boolean assumeSymmetric = false;
- private String passphrase = "";
- private long enforcedKeyId = 0;
+ private ProgressDialogUpdater mProgressDialogUpdater = null;
+ private boolean mAssumeSymmetric = false;
+ private String mPassphrase = "";
+ private long mEnforcedKeyId = 0;
public Builder(Context context, InputData data, OutputStream outStream) {
- this.context = context;
- this.data = data;
- this.outStream = outStream;
+ this.mContext = context;
+ this.mData = data;
+ this.mOutStream = outStream;
}
public Builder progressDialogUpdater(ProgressDialogUpdater progressDialogUpdater) {
- this.progressDialogUpdater = progressDialogUpdater;
+ this.mProgressDialogUpdater = progressDialogUpdater;
return this;
}
public Builder assumeSymmetric(boolean assumeSymmetric) {
- this.assumeSymmetric = assumeSymmetric;
+ this.mAssumeSymmetric = assumeSymmetric;
return this;
}
public Builder passphrase(String passphrase) {
- this.passphrase = passphrase;
+ this.mPassphrase = passphrase;
return this;
}
@@ -134,7 +107,7 @@ public class PgpDecryptVerify {
* @return
*/
public Builder enforcedKeyId(long enforcedKeyId) {
- this.enforcedKeyId = enforcedKeyId;
+ this.mEnforcedKeyId = enforcedKeyId;
return this;
}
@@ -144,14 +117,14 @@ public class PgpDecryptVerify {
}
public void updateProgress(int message, int current, int total) {
- if (progressDialogUpdater != null) {
- progressDialogUpdater.setProgress(message, current, total);
+ if (mProgressDialogUpdater != null) {
+ mProgressDialogUpdater.setProgress(message, current, total);
}
}
public void updateProgress(int current, int total) {
- if (progressDialogUpdater != null) {
- progressDialogUpdater.setProgress(current, total);
+ if (mProgressDialogUpdater != null) {
+ mProgressDialogUpdater.setProgress(current, total);
}
}
@@ -196,7 +169,7 @@ public class PgpDecryptVerify {
public PgpDecryptVerifyResult execute()
throws IOException, PgpGeneralException, PGPException, SignatureException {
// automatically works with ascii armor input and binary
- InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
+ InputStream in = PGPUtil.getDecoderStream(mData.getInputStream());
if (in instanceof ArmoredInputStream) {
ArmoredInputStream aIn = (ArmoredInputStream) in;
// it is ascii armored
@@ -240,7 +213,7 @@ public class PgpDecryptVerify {
}
if (enc == null) {
- throw new PgpGeneralException(context.getString(R.string.error_invalid_data));
+ throw new PgpGeneralException(mContext.getString(R.string.error_invalid_data));
}
InputStream clear;
@@ -250,7 +223,7 @@ public class PgpDecryptVerify {
// TODO: currently we always only look at the first known key or symmetric encryption,
// there might be more...
- if (assumeSymmetric) {
+ if (mAssumeSymmetric) {
PGPPBEEncryptedData pbe = null;
Iterator<?> it = enc.getEncryptedDataObjects();
// find secret key
@@ -264,7 +237,7 @@ public class PgpDecryptVerify {
if (pbe == null) {
throw new PgpGeneralException(
- context.getString(R.string.error_no_symmetric_encryption_packet));
+ mContext.getString(R.string.error_no_symmetric_encryption_packet));
}
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
@@ -273,7 +246,7 @@ public class PgpDecryptVerify {
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build();
PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- passphrase.toCharArray());
+ mPassphrase.toCharArray());
clear = pbe.getDataStream(decryptorFactory);
@@ -290,33 +263,37 @@ public class PgpDecryptVerify {
Object obj = it.next();
if (obj instanceof PGPPublicKeyEncryptedData) {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID());
+ secretKey = ProviderHelper.getPGPSecretKeyByKeyId(mContext, encData.getKeyID());
if (secretKey != null) {
// secret key exists in database
// allow only a specific key for decryption?
- if (enforcedKeyId != 0) {
+ if (mEnforcedKeyId != 0) {
// TODO: improve this code! get master key directly!
- PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, encData.getKeyID());
+ PGPSecretKeyRing secretKeyRing =
+ ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, encData.getKeyID());
long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID();
Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
- Log.d(Constants.TAG, "enforcedKeyId: " + enforcedKeyId);
+ Log.d(Constants.TAG, "enforcedKeyId: " + mEnforcedKeyId);
Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
- if (enforcedKeyId != masterKeyId) {
- throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found));
+ if (mEnforcedKeyId != masterKeyId) {
+ throw new PgpGeneralException(
+ mContext.getString(R.string.error_no_secret_key_found));
}
}
pbe = encData;
// if no passphrase was explicitly set try to get it from the cache service
- if (passphrase == null) {
+ if (mPassphrase == null) {
// returns "" if key has no passphrase
- passphrase = PassphraseCacheService.getCachedPassphrase(context, encData.getKeyID());
+ mPassphrase =
+ PassphraseCacheService.getCachedPassphrase(mContext, encData.getKeyID());
- // if passphrase was not cached, return here indicating that a passphrase is missing!
- if (passphrase == null) {
+ // if passphrase was not cached, return here
+ // indicating that a passphrase is missing!
+ if (mPassphrase == null) {
returnData.setKeyPassphraseNeeded(true);
return returnData;
}
@@ -330,7 +307,7 @@ public class PgpDecryptVerify {
}
if (secretKey == null) {
- throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found));
+ throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
}
currentProgress += 5;
@@ -339,14 +316,14 @@ public class PgpDecryptVerify {
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- passphrase.toCharArray());
+ mPassphrase.toCharArray());
privateKey = secretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
- throw new PGPException(context.getString(R.string.error_wrong_passphrase));
+ throw new PGPException(mContext.getString(R.string.error_wrong_passphrase));
}
if (privateKey == null) {
throw new PgpGeneralException(
- context.getString(R.string.error_could_not_extract_private_key));
+ mContext.getString(R.string.error_could_not_extract_private_key));
}
currentProgress += 5;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
@@ -386,7 +363,7 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
signatureKey = ProviderHelper
- .getPGPPublicKeyByKeyId(context, signature.getKeyID());
+ .getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID();
}
@@ -397,7 +374,7 @@ public class PgpDecryptVerify {
signatureKeyId = signature.getKeyID();
String userId = null;
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(
- context, signatureKeyId);
+ mContext, signatureKeyId);
if (signKeyRing != null) {
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
}
@@ -409,7 +386,8 @@ public class PgpDecryptVerify {
signatureResult.setKeyId(signatureKeyId);
if (signature != null) {
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey);
@@ -444,9 +422,9 @@ public class PgpDecryptVerify {
int n;
// TODO: progress calculation is broken here! Try to rework it based on commented code!
// int progress = 0;
- long startPos = data.getStreamPosition();
+ long startPos = mData.getStreamPosition();
while ((n = dataIn.read(buffer)) > 0) {
- outStream.write(buffer, 0, n);
+ mOutStream.write(buffer, 0, n);
// progress += n;
if (signature != null) {
try {
@@ -460,11 +438,11 @@ public class PgpDecryptVerify {
// unknown size, but try to at least have a moving, slowing down progress bar
// currentProgress = startProgress + (endProgress - startProgress) * progress
// / (progress + 100000);
- if (data.getSize() - startPos == 0) {
+ if (mData.getSize() - startPos == 0) {
currentProgress = endProgress;
} else {
currentProgress = (int) (startProgress + (endProgress - startProgress)
- * (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
+ * (mData.getStreamPosition() - startPos) / (mData.getSize() - startPos));
}
updateProgress(currentProgress, 100);
}
@@ -480,7 +458,7 @@ public class PgpDecryptVerify {
signatureResult.setSignatureOnly(false);
//Now check binding signatures
- boolean validKeyBinding = verifyKeyBinding(context, messageSignature, signatureKey);
+ boolean validKeyBinding = verifyKeyBinding(mContext, messageSignature, signatureKey);
boolean validSignature = signature.verify(messageSignature);
// TODO: implement CERTIFIED!
@@ -499,7 +477,7 @@ public class PgpDecryptVerify {
} else {
// failed
Log.d(Constants.TAG, "Integrity verification: failed!");
- throw new PgpGeneralException(context.getString(R.string.error_integrity_check_failed));
+ throw new PgpGeneralException(mContext.getString(R.string.error_integrity_check_failed));
}
} else {
// no integrity check
@@ -555,21 +533,21 @@ public class PgpDecryptVerify {
out.close();
byte[] clearText = out.toByteArray();
- outStream.write(clearText);
+ mOutStream.write(clearText);
updateProgress(R.string.progress_processing_signature, 60, 100);
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
if (sigList == null) {
- throw new PgpGeneralException(context.getString(R.string.error_corrupt_data));
+ throw new PgpGeneralException(mContext.getString(R.string.error_corrupt_data));
}
PGPSignature signature = null;
long signatureKeyId = 0;
PGPPublicKey signatureKey = null;
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
- signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID());
+ signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID();
}
@@ -579,7 +557,7 @@ public class PgpDecryptVerify {
} else {
signatureKeyId = signature.getKeyID();
String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext,
signatureKeyId);
if (signKeyRing != null) {
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
@@ -623,7 +601,7 @@ public class PgpDecryptVerify {
}
//Now check binding signatures
- boolean validKeyBinding = verifyKeyBinding(context, signature, signatureKey);
+ boolean validKeyBinding = verifyKeyBinding(mContext, signature, signatureKey);
boolean validSignature = signature.verify();
if (validSignature & validKeyBinding) {
@@ -638,7 +616,8 @@ public class PgpDecryptVerify {
return returnData;
}
- private static boolean verifyKeyBinding(Context context, PGPSignature signature, PGPPublicKey signatureKey) {
+ private static boolean verifyKeyBinding(Context context,
+ PGPSignature signature, PGPPublicKey signatureKey) {
long signatureKeyId = signature.getKeyID();
boolean validKeyBinding = false;
@@ -673,7 +652,8 @@ public class PgpDecryptVerify {
//about keys without subkey signing. Can't get it to import a slightly broken one
//either, so we will err on bad subkey binding here.
PGPSignature sig = itr.next();
- if (sig.getKeyID() == masterPublicKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
+ if (sig.getKeyID() == masterPublicKey.getKeyID() &&
+ sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
//check and if ok, check primary key binding.
try {
sig.init(contentVerifierBuilderProvider, masterPublicKey);
@@ -684,34 +664,37 @@ public class PgpDecryptVerify {
continue;
}
- if (validTempSubkeyBinding)
+ if (validTempSubkeyBinding) {
validSubkeyBinding = true;
+ }
if (validTempSubkeyBinding) {
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding)
+ if (validPrimaryKeyBinding) {
break;
+ }
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding)
+ if (validPrimaryKeyBinding) {
break;
+ }
}
}
}
return (validSubkeyBinding & validPrimaryKeyBinding);
}
- private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector Pkts,
- PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
+ private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
+ PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureList eSigList;
- if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
+ if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
try {
- eSigList = Pkts.getEmbeddedSignatures();
+ eSigList = pkts.getEmbeddedSignatures();
} catch (IOException e) {
return false;
} catch (PGPException e) {
@@ -723,8 +706,9 @@ public class PgpDecryptVerify {
try {
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
- if (validPrimaryKeyBinding)
+ if (validPrimaryKeyBinding) {
break;
+ }
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
@@ -743,10 +727,9 @@ public class PgpDecryptVerify {
* @param sig
* @param line
* @throws SignatureException
- * @throws IOException
*/
private static void processLine(PGPSignature sig, byte[] line)
- throws SignatureException, IOException {
+ throws SignatureException {
int length = getLengthWithoutWhiteSpace(line);
if (length > 0) {
sig.update(line, 0, length);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
index 0477c4fdf..d4a4f6075 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
@@ -19,36 +19,35 @@ package org.sufficientlysecure.keychain.pgp;
import android.os.Parcel;
import android.os.Parcelable;
-
import org.openintents.openpgp.OpenPgpSignatureResult;
public class PgpDecryptVerifyResult implements Parcelable {
- boolean symmetricPassphraseNeeded;
- boolean keyPassphraseNeeded;
- OpenPgpSignatureResult signatureResult;
+ boolean mSymmetricPassphraseNeeded;
+ boolean mKeyPassphraseNeeded;
+ OpenPgpSignatureResult mSignatureResult;
public boolean isSymmetricPassphraseNeeded() {
- return symmetricPassphraseNeeded;
+ return mSymmetricPassphraseNeeded;
}
public void setSymmetricPassphraseNeeded(boolean symmetricPassphraseNeeded) {
- this.symmetricPassphraseNeeded = symmetricPassphraseNeeded;
+ this.mSymmetricPassphraseNeeded = symmetricPassphraseNeeded;
}
public boolean isKeyPassphraseNeeded() {
- return keyPassphraseNeeded;
+ return mKeyPassphraseNeeded;
}
public void setKeyPassphraseNeeded(boolean keyPassphraseNeeded) {
- this.keyPassphraseNeeded = keyPassphraseNeeded;
+ this.mKeyPassphraseNeeded = keyPassphraseNeeded;
}
public OpenPgpSignatureResult getSignatureResult() {
- return signatureResult;
+ return mSignatureResult;
}
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
- this.signatureResult = signatureResult;
+ this.mSignatureResult = signatureResult;
}
public PgpDecryptVerifyResult() {
@@ -56,9 +55,9 @@ public class PgpDecryptVerifyResult implements Parcelable {
}
public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) {
- this.symmetricPassphraseNeeded = b.symmetricPassphraseNeeded;
- this.keyPassphraseNeeded = b.keyPassphraseNeeded;
- this.signatureResult = b.signatureResult;
+ this.mSymmetricPassphraseNeeded = b.mSymmetricPassphraseNeeded;
+ this.mKeyPassphraseNeeded = b.mKeyPassphraseNeeded;
+ this.mSignatureResult = b.mSignatureResult;
}
@@ -67,17 +66,17 @@ public class PgpDecryptVerifyResult implements Parcelable {
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeByte((byte) (symmetricPassphraseNeeded ? 1 : 0));
- dest.writeByte((byte) (keyPassphraseNeeded ? 1 : 0));
- dest.writeParcelable(signatureResult, 0);
+ dest.writeByte((byte) (mSymmetricPassphraseNeeded ? 1 : 0));
+ dest.writeByte((byte) (mKeyPassphraseNeeded ? 1 : 0));
+ dest.writeParcelable(mSignatureResult, 0);
}
public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() {
public PgpDecryptVerifyResult createFromParcel(final Parcel source) {
PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult();
- vr.symmetricPassphraseNeeded = source.readByte() == 1;
- vr.keyPassphraseNeeded = source.readByte() == 1;
- vr.signatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
+ vr.mSymmetricPassphraseNeeded = source.readByte() == 1;
+ vr.mKeyPassphraseNeeded = source.readByte() == 1;
+ vr.mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
return vr;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 7ac904d89..2680d77af 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -17,22 +17,10 @@
package org.sufficientlysecure.keychain.pgp;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-import java.security.SecureRandom;
-import java.util.Iterator;
-import java.util.regex.Pattern;
-
-import org.spongycastle.openpgp.PGPEncryptedDataList;
-import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPUtil;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import org.spongycastle.openpgp.*;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -42,21 +30,25 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.security.SecureRandom;
+import java.util.Iterator;
+import java.util.regex.Pattern;
public class PgpHelper {
- public static Pattern PGP_MESSAGE = Pattern.compile(
+ public static final Pattern PGP_MESSAGE = Pattern.compile(
".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
- public static Pattern PGP_SIGNED_MESSAGE = Pattern
+ public static final Pattern PGP_SIGNED_MESSAGE = Pattern
.compile(
".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
Pattern.DOTALL);
- public static Pattern PGP_PUBLIC_KEY = Pattern.compile(
+ public static final Pattern PGP_PUBLIC_KEY = Pattern.compile(
".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
Pattern.DOTALL);
@@ -140,7 +132,7 @@ public class PgpHelper {
/**
* Generate a random filename
- *
+ *
* @param length
* @return
*/
@@ -170,7 +162,7 @@ public class PgpHelper {
/**
* Go once through stream to get length of stream. The length is later used to display progress
* when encrypting/decrypting
- *
+ *
* @param in
* @return
* @throws IOException
@@ -187,17 +179,16 @@ public class PgpHelper {
/**
* Deletes file securely by overwriting it with random data before deleting it.
- *
+ * <p/>
* TODO: Does this really help on flash storage?
- *
+ *
* @param context
* @param progress
* @param file
- * @throws FileNotFoundException
* @throws IOException
*/
public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file)
- throws FileNotFoundException, IOException {
+ throws IOException {
long length = file.length();
SecureRandom random = new SecureRandom();
RandomAccessFile raf = new RandomAccessFile(file, "rws");
@@ -207,8 +198,9 @@ public class PgpHelper {
int pos = 0;
String msg = context.getString(R.string.progress_deleting_securely, file.getName());
while (pos < length) {
- if (progress != null)
+ if (progress != null) {
progress.setProgress(msg, (int) (100 * pos / length), 100);
+ }
random.nextBytes(data);
raf.write(data);
pos += data.length;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index 2495a212c..0e0fdec83 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -17,24 +17,11 @@
package org.sufficientlysecure.keychain.pgp;
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Environment;
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.*;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -43,28 +30,37 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
-import org.sufficientlysecure.keychain.util.HkpKeyServer;
-import org.sufficientlysecure.keychain.util.InputData;
-import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.*;
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
-import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Environment;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.Provider;
+import java.util.ArrayList;
+import java.util.List;
public class PgpImportExport {
+
private Context mContext;
private ProgressDialogUpdater mProgress;
+ private KeychainServiceListener mKeychainServiceListener;
+
public PgpImportExport(Context context, ProgressDialogUpdater progress) {
super();
this.mContext = context;
this.mProgress = progress;
}
+ public PgpImportExport(Context context,
+ ProgressDialogUpdater progress, KeychainServiceListener keychainListener) {
+ super();
+ this.mContext = context;
+ this.mProgress = progress;
+ this.mKeychainServiceListener = keychainListener;
+ }
+
public void updateProgress(int message, int current, int total) {
if (mProgress != null) {
mProgress.setProgress(message, current, total);
@@ -85,8 +81,9 @@ public class PgpImportExport {
public boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ArmoredOutputStream aos = new ArmoredOutputStream(bos);
+ ArmoredOutputStream aos = null;
try {
+ aos = new ArmoredOutputStream(bos);
aos.write(keyring.getEncoded());
aos.close();
@@ -101,7 +98,8 @@ public class PgpImportExport {
return false;
} finally {
try {
- bos.close();
+ if (aos != null) { aos.close(); }
+ if (bos != null) { bos.close(); }
} catch (IOException e) {
}
}
@@ -161,59 +159,68 @@ public class PgpImportExport {
return returnData;
}
- public Bundle exportKeyRings(ArrayList<Long> keyRingMasterKeyIds, int keyType,
- OutputStream outStream) throws PgpGeneralException, FileNotFoundException,
+ public Bundle exportKeyRings(ArrayList<Long> publicKeyRingMasterIds, ArrayList<Long> secretKeyRingMasterIds,
+ OutputStream outStream) throws PgpGeneralException,
PGPException, IOException {
Bundle returnData = new Bundle();
+ int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size();
+ int progress = 0;
+
updateProgress(
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
- keyRingMasterKeyIds.size()), 0, 100);
+ masterKeyIdsSize), 0, 100);
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new PgpGeneralException(
mContext.getString(R.string.error_external_storage_not_ready));
}
+ // For each public masterKey id
+ for (long pubKeyMasterId : publicKeyRingMasterIds) {
+ progress++;
+ // Create an output stream
+ ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
+ arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
+
+ updateProgress(progress * 100 / masterKeyIdsSize, 100);
+ PGPPublicKeyRing publicKeyRing =
+ ProviderHelper.getPGPPublicKeyRingByMasterKeyId(mContext, pubKeyMasterId);
+
+ if (publicKeyRing != null) {
+ publicKeyRing.encode(arOutStream);
+ }
- if (keyType == Id.type.secret_key) {
- ArmoredOutputStream outSec = new ArmoredOutputStream(outStream);
- outSec.setHeader("Version", PgpHelper.getFullVersion(mContext));
-
- for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
- updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
+ if (mKeychainServiceListener.hasServiceStopped()) {
+ arOutStream.close();
+ return null;
+ }
- PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
- mContext, keyRingMasterKeyIds.get(i));
+ arOutStream.close();
+ }
- if (secretKeyRing != null) {
- secretKeyRing.encode(outSec);
- }
- }
- outSec.close();
- } else {
- // export public keyrings...
- ArmoredOutputStream outPub = new ArmoredOutputStream(outStream);
- outPub.setHeader("Version", PgpHelper.getFullVersion(mContext));
-
- for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
- // double the needed time if exporting both public and secret parts
- if (keyType == Id.type.secret_key) {
- updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
- } else {
- updateProgress(i * 100 / keyRingMasterKeyIds.size(), 100);
- }
+ // For each secret masterKey id
+ for (long secretKeyMasterId : secretKeyRingMasterIds) {
+ progress++;
+ // Create an output stream
+ ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
+ arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
- PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(
- mContext, keyRingMasterKeyIds.get(i));
+ updateProgress(progress * 100 / masterKeyIdsSize, 100);
+ PGPSecretKeyRing secretKeyRing =
+ ProviderHelper.getPGPSecretKeyRingByMasterKeyId(mContext, secretKeyMasterId);
- if (publicKeyRing != null) {
- publicKeyRing.encode(outPub);
- }
+ if (secretKeyRing != null) {
+ secretKeyRing.encode(arOutStream);
+ }
+ if (mKeychainServiceListener.hasServiceStopped()) {
+ arOutStream.close();
+ return null;
}
- outPub.close();
+
+ arOutStream.close();
}
- returnData.putInt(KeychainIntentService.RESULT_EXPORT, keyRingMasterKeyIds.size());
+ returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize);
updateProgress(R.string.progress_done, 100, 100);
@@ -234,7 +241,7 @@ public class PgpImportExport {
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
secretKeyRing.getSecretKeys())) {
if (!testSecretKey.isMasterKey()) {
- if (PgpKeyHelper.isSecretKeyPrivateEmpty(testSecretKey)) {
+ if (testSecretKey.isPrivateKeyEmpty()) {
// this is bad, something is very wrong...
save = false;
status = Id.return_value.bad;
@@ -255,8 +262,9 @@ public class PgpImportExport {
}
newPubRing = PGPPublicKeyRing.insertPublicKey(newPubRing, key);
}
- if (newPubRing != null)
+ if (newPubRing != null) {
ProviderHelper.saveKeyRing(mContext, newPubRing);
+ }
// TODO: remove status returns, use exceptions!
status = Id.return_value.ok;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index c1c6f3088..b4bf0747f 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -17,28 +17,27 @@
package org.sufficientlysecure.keychain.pgp;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Locale;
-import java.util.Vector;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import android.content.Context;
+import android.graphics.Color;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.*;
+import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
-import android.content.Context;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class PgpKeyHelper {
@@ -466,55 +465,30 @@ public class PgpKeyHelper {
String algorithmStr;
switch (algorithm) {
- case PGPPublicKey.RSA_ENCRYPT:
- case PGPPublicKey.RSA_GENERAL:
- case PGPPublicKey.RSA_SIGN: {
- algorithmStr = "RSA";
- break;
- }
-
- case PGPPublicKey.DSA: {
- algorithmStr = "DSA";
- break;
- }
-
- case PGPPublicKey.ELGAMAL_ENCRYPT:
- case PGPPublicKey.ELGAMAL_GENERAL: {
- algorithmStr = "ElGamal";
- break;
- }
+ case PGPPublicKey.RSA_ENCRYPT:
+ case PGPPublicKey.RSA_GENERAL:
+ case PGPPublicKey.RSA_SIGN: {
+ algorithmStr = "RSA";
+ break;
+ }
- default: {
- algorithmStr = "Unknown";
- break;
- }
- }
- return algorithmStr + ", " + keySize + " bit";
- }
+ case PGPPublicKey.DSA: {
+ algorithmStr = "DSA";
+ break;
+ }
- /**
- * Converts fingerprint to hex with whitespaces after 4 characters
- *
- * @param fp
- * @return
- */
- public static String convertFingerprintToHex(byte[] fp, boolean chunked) {
- String fingerPrint = "";
- for (int i = 0; i < fp.length; ++i) {
- if (chunked && i != 0 && i % 10 == 0) {
- fingerPrint += " ";
- } else if (chunked && i != 0 && i % 2 == 0) {
- fingerPrint += " ";
+ case PGPPublicKey.ELGAMAL_ENCRYPT:
+ case PGPPublicKey.ELGAMAL_GENERAL: {
+ algorithmStr = "ElGamal";
+ break;
}
- String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(Locale.US);
- while (chunk.length() < 2) {
- chunk = "0" + chunk;
+
+ default: {
+ algorithmStr = "Unknown";
+ break;
}
- fingerPrint += chunk;
}
-
- return fingerPrint;
-
+ return algorithmStr + ", " + keySize + " bit";
}
public static String getFingerPrint(Context context, long keyId) {
@@ -529,55 +503,150 @@ public class PgpKeyHelper {
key = secretKey.getPublicKey();
}
- return convertFingerprintToHex(key.getFingerprint(), true);
- }
-
- public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) {
- return secretKey.isPrivateKeyEmpty();
+ return convertFingerprintToHex(key.getFingerprint());
}
-// public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) {
-// PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
-// if (secretKey == null) {
-// Log.e(Constants.TAG, "Key could not be found!");
-// return false; // could be a public key, assume it is not empty
-// }
-// return isSecretKeyPrivateEmpty(secretKey);
-// }
+ /**
+ * Converts fingerprint to hex (optional: with whitespaces after 4 characters)
+ * <p/>
+ * Fingerprint is shown using lowercase characters. Studies have shown that humans can
+ * better differentiate between numbers and letters when letters are lowercase.
+ *
+ * @param fingerprint
+ * @param split split into 4 character chunks
+ * @return
+ */
+ public static String convertFingerprintToHex(byte[] fingerprint) {
+ String hexString = Hex.toHexString(fingerprint);
- public static String convertKeyIdToHex(long keyId) {
- String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US);
- while (fingerPrint.length() < 8) {
- fingerPrint = "0" + fingerPrint;
- }
- return fingerPrint;
+ return hexString;
}
/**
- * TODO: documentation
- *
+ * Convert key id from long to 64 bit hex string
+ * <p/>
+ * V4: "The Key ID is the low-order 64 bits of the fingerprint"
+ * <p/>
+ * see http://tools.ietf.org/html/rfc4880#section-12.2
+ *
* @param keyId
* @return
*/
- public static String convertKeyToHex(long keyId) {
- return convertKeyIdToHex(keyId >> 32) + convertKeyIdToHex(keyId);
+ public static String convertKeyIdToHex(long keyId) {
+ long upper = keyId >> 32;
+ if (upper == 0) {
+ // this is a short key id
+ return convertKeyIdToHexShort(keyId);
+ }
+ return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
}
- public static long convertHexToKeyId(String data) {
- int len = data.length();
- String s2 = data.substring(len - 8);
- String s1 = data.substring(0, len - 8);
- return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
+ public static String convertKeyIdToHexShort(long keyId) {
+ return "0x" + convertKeyIdToHex32bit(keyId);
+ }
+
+ private static String convertKeyIdToHex32bit(long keyId) {
+ String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.US);
+ while (hexString.length() < 8) {
+ hexString = "0" + hexString;
+ }
+ return hexString;
+ }
+
+
+ public static SpannableStringBuilder colorizeFingerprint(String fingerprint) {
+ // split by 4 characters
+ fingerprint = fingerprint.replaceAll("(.{4})(?!$)", "$1 ");
+
+ // add line breaks to have a consistent "image" that can be recognized
+ char[] chars = fingerprint.toCharArray();
+ chars[24] = '\n';
+ fingerprint = String.valueOf(chars);
+
+ SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint);
+ try {
+ // for each 4 characters of the fingerprint + 1 space
+ for (int i = 0; i < fingerprint.length(); i += 5) {
+ int spanEnd = Math.min(i + 4, fingerprint.length());
+ String fourChars = fingerprint.substring(i, spanEnd);
+
+ int raw = Integer.parseInt(fourChars, 16);
+ byte[] bytes = {(byte) ((raw >> 8) & 0xff - 128), (byte) (raw & 0xff - 128)};
+ int[] color = getRgbForData(bytes);
+ int r = color[0];
+ int g = color[1];
+ int b = color[2];
+
+ // we cannot change black by multiplication, so adjust it to an almost-black grey,
+ // which will then be brightened to the minimal brightness level
+ if (r == 0 && g == 0 && b == 0) {
+ r = 1;
+ g = 1;
+ b = 1;
+ }
+
+ // Convert rgb to brightness
+ double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
+
+ // If a color is too dark to be seen on black,
+ // then brighten it up to a minimal brightness.
+ if (brightness < 80) {
+ double factor = 80.0 / brightness;
+ r = Math.min(255, (int) (r * factor));
+ g = Math.min(255, (int) (g * factor));
+ b = Math.min(255, (int) (b * factor));
+
+ // If it is too light, then darken it to a respective maximal brightness.
+ } else if (brightness > 180) {
+ double factor = 180.0 / brightness;
+ r = (int) (r * factor);
+ g = (int) (g * factor);
+ b = (int) (b * factor);
+ }
+
+ // Create a foreground color with the 3 digest integers as RGB
+ // and then converting that int to hex to use as a color
+ sb.setSpan(new ForegroundColorSpan(Color.rgb(r, g, b)),
+ i, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "Colorization failed", e);
+ // if anything goes wrong, then just display the fingerprint without colour,
+ // instead of partially correct colour or wrong colours
+ return new SpannableStringBuilder(fingerprint);
+ }
+
+ return sb;
+ }
+
+ /**
+ * Converts the given bytes to a unique RGB color using SHA1 algorithm
+ *
+ * @param bytes
+ * @return an integer array containing 3 numeric color representations (Red, Green, Black)
+ * @throws java.security.NoSuchAlgorithmException
+ * @throws java.security.DigestException
+ */
+ private static int[] getRgbForData(byte[] bytes) throws NoSuchAlgorithmException, DigestException {
+ MessageDigest md = MessageDigest.getInstance("SHA1");
+
+ md.update(bytes);
+ byte[] digest = md.digest();
+
+ int[] result = {((int) digest[0] + 256) % 256,
+ ((int) digest[1] + 256) % 256,
+ ((int) digest[2] + 256) % 256};
+ return result;
}
/**
* Splits userId string into naming part, email part, and comment part
- *
+ *
* @param userId
* @return array with naming (0), email (1), comment (2)
*/
public static String[] splitUserId(String userId) {
- String[] result = new String[] { null, null, null };
+ String[] result = new String[]{null, null, null};
if (userId == null || userId.equals("")) {
return result;
@@ -598,7 +667,6 @@ public class PgpKeyHelper {
result[0] = matcher.group(1);
result[1] = matcher.group(3);
result[2] = matcher.group(2);
- return result;
}
return result;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index b7f84ccdc..05ca99578 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -30,35 +30,19 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.TimeZone;
-
+import android.content.Context;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.spec.ElGamalParameterSpec;
-import org.spongycastle.openpgp.PGPEncryptedData;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPKeyPair;
-import org.spongycastle.openpgp.PGPKeyRingGenerator;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.*;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.*;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -70,20 +54,34 @@ import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
import android.content.Context;
import android.util.Pair;
+import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Primes;
+import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TimeZone;
public class PgpKeyOperation {
private final Context mContext;
private final ProgressDialogUpdater mProgress;
- private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] {
+ private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5,
- SymmetricKeyAlgorithmTags.TRIPLE_DES };
- private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1,
- HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160 };
- private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] {
+ SymmetricKeyAlgorithmTags.TRIPLE_DES};
+ private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{HashAlgorithmTags.SHA1,
+ HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160};
+ private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{
CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
- CompressionAlgorithmTags.ZIP };
+ CompressionAlgorithmTags.ZIP};
public PgpKeyOperation(Context context, ProgressDialogUpdater progress) {
super();
@@ -104,8 +102,9 @@ public class PgpKeyOperation {
}
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
- boolean isMasterKey) throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
- PgpGeneralException, InvalidAlgorithmParameterException {
+ boolean isMasterKey)
+ throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
+ PgpGeneralException, InvalidAlgorithmParameterException {
if (keySize < 512) {
throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
@@ -119,41 +118,41 @@ public class PgpKeyOperation {
KeyPairGenerator keyGen;
switch (algorithmChoice) {
- case Id.choice.algorithm.dsa: {
- keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- keyGen.initialize(keySize, new SecureRandom());
- algorithm = PGPPublicKey.DSA;
- break;
- }
-
- case Id.choice.algorithm.elgamal: {
- if (isMasterKey) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_master_key_must_not_be_el_gamal));
+ case Id.choice.algorithm.dsa: {
+ keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ keyGen.initialize(keySize, new SecureRandom());
+ algorithm = PGPPublicKey.DSA;
+ break;
}
- keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- BigInteger p = Primes.getBestPrime(keySize);
- BigInteger g = new BigInteger("2");
- ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
+ case Id.choice.algorithm.elgamal: {
+ if (isMasterKey) {
+ throw new PgpGeneralException(
+ mContext.getString(R.string.error_master_key_must_not_be_el_gamal));
+ }
+ keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ BigInteger p = Primes.getBestPrime(keySize);
+ BigInteger g = new BigInteger("2");
- keyGen.initialize(elParams);
- algorithm = PGPPublicKey.ELGAMAL_ENCRYPT;
- break;
- }
+ ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
- case Id.choice.algorithm.rsa: {
- keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- keyGen.initialize(keySize, new SecureRandom());
+ keyGen.initialize(elParams);
+ algorithm = PGPPublicKey.ELGAMAL_ENCRYPT;
+ break;
+ }
- algorithm = PGPPublicKey.RSA_GENERAL;
- break;
- }
+ case Id.choice.algorithm.rsa: {
+ keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ keyGen.initialize(keySize, new SecureRandom());
- default: {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_unknown_algorithm_choice));
- }
+ algorithm = PGPPublicKey.RSA_GENERAL;
+ break;
+ }
+
+ default: {
+ throw new PgpGeneralException(
+ mContext.getString(R.string.error_unknown_algorithm_choice));
+ }
}
// build new key pair
@@ -170,11 +169,10 @@ public class PgpKeyOperation {
return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, isMasterKey, keyEncryptor);
-
}
public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
- String newPassPhrase) throws IOException, PGPException {
+ String newPassPhrase) throws IOException, PGPException {
updateProgress(R.string.progress_building_key, 0, 100);
if (oldPassPhrase == null) {
@@ -221,15 +219,15 @@ public class PgpKeyOperation {
updateProgress(R.string.progress_certifying_master_key, 20, 100);
int user_id_index = 0;
for (String userId : userIds) {
- PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
- sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
- PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
+ PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
user_id_index++;
}
@@ -280,7 +278,7 @@ public class PgpKeyOperation {
updateProgress(R.string.progress_adding_sub_keys, 40, 100);
for (int i = 1; i < keys.size(); ++i) {
- updateProgress(40 + 50 * (i - 1) / (keys.size() - 1), 100);
+ updateProgress(40 + 40 * (i - 1) / (keys.size() - 1), 100);
PGPSecretKey subKey = keys.get(i);
PGPPublicKey subPublicKey = subKey.getPublicKey();
@@ -345,7 +343,7 @@ public class PgpKeyOperation {
updateProgress(R.string.progress_done, 100, 100);
}
- public void buildSecretKey(SaveKeyringParcel saveParcel) throws PgpGeneralException,
+ public void buildSecretKey (SaveKeyringParcel saveParcel)throws PgpGeneralException,
PGPException, SignatureException, IOException {
updateProgress(R.string.progress_building_key, 0, 100);
@@ -387,7 +385,7 @@ public class PgpKeyOperation {
Todo
identify more things which need to be preserved - e.g. trust levels?
user attributes
- */
+ */
if (saveParcel.deletedKeys != null) {
for (PGPSecretKey dKey : saveParcel.deletedKeys) {
@@ -546,7 +544,7 @@ public class PgpKeyOperation {
unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor);
for (int i = 1; i < saveParcel.keys.size(); ++i) {
- updateProgress(40 + 50 * i/ saveParcel.keys.size(), 100);
+ updateProgress(40 + 50 * i / saveParcel.keys.size(), 100);
if (saveParcel.moddedKeys[i]) {
PGPSecretKey subKey = saveParcel.keys.get(i);
PGPPublicKey subPublicKey = subKey.getPublicKey();
@@ -558,8 +556,8 @@ public class PgpKeyOperation {
"".toCharArray());
} else {
keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- saveParcel.oldPassPhrase.toCharArray());
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.oldPassPhrase.toCharArray());
}
PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2);
PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey);
@@ -642,50 +640,80 @@ public class PgpKeyOperation {
mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew);
updateProgress(R.string.progress_saving_key_ring, 90, 100);
+ /* additional handy debug info
+
+ Log.d(Constants.TAG, " ------- in private key -------");
+
+ for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
+ for(PGPSignature sig : new IterableIterator<PGPSignature>(secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ Log.d(Constants.TAG, "sig: " + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
+ }
+
+ }
+
+ Log.d(Constants.TAG, " ------- in public key -------");
+
+ for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
+ for(PGPSignature sig : new IterableIterator<PGPSignature>(publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ Log.d(Constants.TAG, "sig: " + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
+ }
+ }
+
+ */
+
+
ProviderHelper.saveKeyRing(mContext, mKR);
ProviderHelper.saveKeyRing(mContext, pKR);
updateProgress(R.string.progress_done, 100, 100);
}
- public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, String passphrase)
- throws PgpGeneralException, PGPException, SignatureException {
+ public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, List<String> userIds, String passphrase)
+ throws PgpGeneralException, NoSuchAlgorithmException, NoSuchProviderException,
+ PGPException, SignatureException {
if (passphrase == null) {
throw new PgpGeneralException("Unable to obtain passphrase");
} else {
- PGPPublicKeyRing pubring = ProviderHelper
- .getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
- PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
- if (certificationKey == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
- }
+ // create a signatureGenerator from the supplied masterKeyId and passphrase
+ PGPSignatureGenerator signatureGenerator; {
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_could_not_extract_private_key));
- }
-
- // TODO: SHA256 fixed?
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
+ if (certificationKey == null) {
+ throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
+ }
- PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
- contentSignerBuilder);
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
+ if (signaturePrivateKey == null) {
+ throw new PgpGeneralException(
+ mContext.getString(R.string.error_could_not_extract_private_key));
+ }
- signatureGenerator.init(PGPSignature.DIRECT_KEY, signaturePrivateKey);
+ // TODO: SHA256 fixed?
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
+ }
- PGPSignatureSubpacketVector packetVector = spGen.generate();
- signatureGenerator.setHashedSubpackets(packetVector);
+ { // supply signatureGenerator with a SubpacketVector
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketVector packetVector = spGen.generate();
+ signatureGenerator.setHashedSubpackets(packetVector);
+ }
- PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId),
- signatureGenerator.generate());
+ // fetch public key ring, add the certification and return it
+ PGPPublicKeyRing pubring = ProviderHelper
+ .getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
+ PGPPublicKey signedKey = pubring.getPublicKey(pubKeyId);
+ for(String userId : new IterableIterator<String>(userIds.iterator())) {
+ PGPSignature sig = signatureGenerator.generateCertification(userId, signedKey);
+ signedKey = PGPPublicKey.addCertification(signedKey, userId, sig);
+ }
pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
return pubring;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index ba1182c1b..737e9c75d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -18,28 +18,11 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
-
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream;
-import org.spongycastle.openpgp.PGPCompressedDataGenerator;
-import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPLiteralData;
-import org.spongycastle.openpgp.PGPLiteralDataGenerator;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.spongycastle.openpgp.PGPV3SignatureGenerator;
+import org.spongycastle.openpgp.*;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
+import org.spongycastle.openpgp.operator.jcajce.*;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -49,11 +32,7 @@ import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
+import java.io.*;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
@@ -63,110 +42,110 @@ import java.util.Date;
* This class uses a Builder pattern!
*/
public class PgpSignEncrypt {
- private Context context;
- private InputData data;
- private OutputStream outStream;
-
- private ProgressDialogUpdater progress;
- private boolean enableAsciiArmorOutput;
- private int compressionId;
- private long[] encryptionKeyIds;
- private String encryptionPassphrase;
- private int symmetricEncryptionAlgorithm;
- private long signatureKeyId;
- private int signatureHashAlgorithm;
- private boolean signatureForceV3;
- private String signaturePassphrase;
+ private Context mContext;
+ private InputData mData;
+ private OutputStream mOutStream;
+
+ private ProgressDialogUpdater mProgress;
+ private boolean mEnableAsciiArmorOutput;
+ private int mCompressionId;
+ private long[] mEncryptionKeyIds;
+ private String mEncryptionPassphrase;
+ private int mSymmetricEncryptionAlgorithm;
+ private long mSignatureKeyId;
+ private int mSignatureHashAlgorithm;
+ private boolean mSignatureForceV3;
+ private String mSignaturePassphrase;
private PgpSignEncrypt(Builder builder) {
// private Constructor can only be called from Builder
- this.context = builder.context;
- this.data = builder.data;
- this.outStream = builder.outStream;
-
- this.progress = builder.progress;
- this.enableAsciiArmorOutput = builder.enableAsciiArmorOutput;
- this.compressionId = builder.compressionId;
- this.encryptionKeyIds = builder.encryptionKeyIds;
- this.encryptionPassphrase = builder.encryptionPassphrase;
- this.symmetricEncryptionAlgorithm = builder.symmetricEncryptionAlgorithm;
- this.signatureKeyId = builder.signatureKeyId;
- this.signatureHashAlgorithm = builder.signatureHashAlgorithm;
- this.signatureForceV3 = builder.signatureForceV3;
- this.signaturePassphrase = builder.signaturePassphrase;
+ this.mContext = builder.mContext;
+ this.mData = builder.mData;
+ this.mOutStream = builder.mOutStream;
+
+ this.mProgress = builder.mProgress;
+ this.mEnableAsciiArmorOutput = builder.mEnableAsciiArmorOutput;
+ this.mCompressionId = builder.mCompressionId;
+ this.mEncryptionKeyIds = builder.mEncryptionKeyIds;
+ this.mEncryptionPassphrase = builder.mEncryptionPassphrase;
+ this.mSymmetricEncryptionAlgorithm = builder.mSymmetricEncryptionAlgorithm;
+ this.mSignatureKeyId = builder.mSignatureKeyId;
+ this.mSignatureHashAlgorithm = builder.mSignatureHashAlgorithm;
+ this.mSignatureForceV3 = builder.mSignatureForceV3;
+ this.mSignaturePassphrase = builder.mSignaturePassphrase;
}
public static class Builder {
// mandatory parameter
- private Context context;
- private InputData data;
- private OutputStream outStream;
+ private Context mContext;
+ private InputData mData;
+ private OutputStream mOutStream;
// optional
- private ProgressDialogUpdater progress = null;
- private boolean enableAsciiArmorOutput = false;
- private int compressionId = Id.choice.compression.none;
- private long[] encryptionKeyIds = new long[0];
- private String encryptionPassphrase = null;
- private int symmetricEncryptionAlgorithm = 0;
- private long signatureKeyId = Id.key.none;
- private int signatureHashAlgorithm = 0;
- private boolean signatureForceV3 = false;
- private String signaturePassphrase = null;
+ private ProgressDialogUpdater mProgress = null;
+ private boolean mEnableAsciiArmorOutput = false;
+ private int mCompressionId = Id.choice.compression.none;
+ private long[] mEncryptionKeyIds = new long[0];
+ private String mEncryptionPassphrase = null;
+ private int mSymmetricEncryptionAlgorithm = 0;
+ private long mSignatureKeyId = Id.key.none;
+ private int mSignatureHashAlgorithm = 0;
+ private boolean mSignatureForceV3 = false;
+ private String mSignaturePassphrase = null;
public Builder(Context context, InputData data, OutputStream outStream) {
- this.context = context;
- this.data = data;
- this.outStream = outStream;
+ this.mContext = context;
+ this.mData = data;
+ this.mOutStream = outStream;
}
public Builder progress(ProgressDialogUpdater progress) {
- this.progress = progress;
+ this.mProgress = progress;
return this;
}
public Builder enableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
- this.enableAsciiArmorOutput = enableAsciiArmorOutput;
+ this.mEnableAsciiArmorOutput = enableAsciiArmorOutput;
return this;
}
public Builder compressionId(int compressionId) {
- this.compressionId = compressionId;
+ this.mCompressionId = compressionId;
return this;
}
public Builder encryptionKeyIds(long[] encryptionKeyIds) {
- this.encryptionKeyIds = encryptionKeyIds;
+ this.mEncryptionKeyIds = encryptionKeyIds;
return this;
}
public Builder encryptionPassphrase(String encryptionPassphrase) {
- this.encryptionPassphrase = encryptionPassphrase;
+ this.mEncryptionPassphrase = encryptionPassphrase;
return this;
}
public Builder symmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
- this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
+ this.mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
return this;
}
public Builder signatureKeyId(long signatureKeyId) {
- this.signatureKeyId = signatureKeyId;
+ this.mSignatureKeyId = signatureKeyId;
return this;
}
public Builder signatureHashAlgorithm(int signatureHashAlgorithm) {
- this.signatureHashAlgorithm = signatureHashAlgorithm;
+ this.mSignatureHashAlgorithm = signatureHashAlgorithm;
return this;
}
public Builder signatureForceV3(boolean signatureForceV3) {
- this.signatureForceV3 = signatureForceV3;
+ this.mSignatureForceV3 = signatureForceV3;
return this;
}
public Builder signaturePassphrase(String signaturePassphrase) {
- this.signaturePassphrase = signaturePassphrase;
+ this.mSignaturePassphrase = signaturePassphrase;
return this;
}
@@ -176,14 +155,14 @@ public class PgpSignEncrypt {
}
public void updateProgress(int message, int current, int total) {
- if (progress != null) {
- progress.setProgress(message, current, total);
+ if (mProgress != null) {
+ mProgress.setProgress(message, current, total);
}
}
public void updateProgress(int current, int total) {
- if (progress != null) {
- progress.setProgress(current, total);
+ if (mProgress != null) {
+ mProgress.setProgress(current, total);
}
}
@@ -201,17 +180,17 @@ public class PgpSignEncrypt {
throws IOException, PgpGeneralException, PGPException, NoSuchProviderException,
NoSuchAlgorithmException, SignatureException {
- boolean enableSignature = signatureKeyId != Id.key.none;
- boolean enableEncryption = (encryptionKeyIds.length != 0 || encryptionPassphrase != null);
- boolean enableCompression = (enableEncryption && compressionId != Id.choice.compression.none);
+ boolean enableSignature = mSignatureKeyId != Id.key.none;
+ boolean enableEncryption = (mEncryptionKeyIds.length != 0 || mEncryptionPassphrase != null);
+ boolean enableCompression = (enableEncryption && mCompressionId != Id.choice.compression.none);
Log.d(Constants.TAG, "enableSignature:" + enableSignature
+ "\nenableEncryption:" + enableEncryption
+ "\nenableCompression:" + enableCompression
- + "\nenableAsciiArmorOutput:" + enableAsciiArmorOutput);
+ + "\nenableAsciiArmorOutput:" + mEnableAsciiArmorOutput);
int signatureType;
- if (enableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
+ if (mEnableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
// for sign-only ascii text
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
} else {
@@ -220,12 +199,12 @@ public class PgpSignEncrypt {
ArmoredOutputStream armorOut = null;
OutputStream out;
- if (enableAsciiArmorOutput) {
- armorOut = new ArmoredOutputStream(outStream);
- armorOut.setHeader("Version", PgpHelper.getFullVersion(context));
+ if (mEnableAsciiArmorOutput) {
+ armorOut = new ArmoredOutputStream(mOutStream);
+ armorOut.setHeader("Version", PgpHelper.getFullVersion(mContext));
out = armorOut;
} else {
- out = outStream;
+ out = mOutStream;
}
/* Get keys for signature generation for later usage */
@@ -233,25 +212,25 @@ public class PgpSignEncrypt {
PGPSecretKeyRing signingKeyRing = null;
PGPPrivateKey signaturePrivateKey = null;
if (enableSignature) {
- signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId);
- signingKey = PgpKeyHelper.getSigningKey(context, signatureKeyId);
+ signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, mSignatureKeyId);
+ signingKey = PgpKeyHelper.getSigningKey(mContext, mSignatureKeyId);
if (signingKey == null) {
- throw new PgpGeneralException(context.getString(R.string.error_signature_failed));
+ throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
}
- if (signaturePassphrase == null) {
+ if (mSignaturePassphrase == null) {
throw new PgpGeneralException(
- context.getString(R.string.error_no_signature_passphrase));
+ mContext.getString(R.string.error_no_signature_passphrase));
}
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray());
signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
if (signaturePrivateKey == null) {
throw new PgpGeneralException(
- context.getString(R.string.error_could_not_extract_private_key));
+ mContext.getString(R.string.error_could_not_extract_private_key));
}
}
updateProgress(R.string.progress_preparing_streams, 5, 100);
@@ -261,23 +240,23 @@ public class PgpSignEncrypt {
if (enableEncryption) {
// has Integrity packet enabled!
JcePGPDataEncryptorBuilder encryptorBuilder =
- new JcePGPDataEncryptorBuilder(symmetricEncryptionAlgorithm)
+ new JcePGPDataEncryptorBuilder(mSymmetricEncryptionAlgorithm)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
.setWithIntegrityPacket(true);
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
- if (encryptionKeyIds.length == 0) {
+ if (mEncryptionKeyIds.length == 0) {
// Symmetric encryption
Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption");
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
- new JcePBEKeyEncryptionMethodGenerator(encryptionPassphrase.toCharArray());
+ new JcePBEKeyEncryptionMethodGenerator(mEncryptionPassphrase.toCharArray());
cPk.addMethod(symmetricEncryptionGenerator);
} else {
// Asymmetric encryption
- for (long id : encryptionKeyIds) {
- PGPPublicKey key = PgpKeyHelper.getEncryptPublicKey(context, id);
+ for (long id : mEncryptionKeyIds) {
+ PGPPublicKey key = PgpKeyHelper.getEncryptPublicKey(mContext, id);
if (key != null) {
JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
new JcePublicKeyKeyEncryptionMethodGenerator(key);
@@ -295,10 +274,10 @@ public class PgpSignEncrypt {
// content signer based on signing key algorithm and chosen hash algorithm
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm)
+ signingKey.getPublicKey().getAlgorithm(), mSignatureHashAlgorithm)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- if (signatureForceV3) {
+ if (mSignatureForceV3) {
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
signatureV3Generator.init(signatureType, signaturePrivateKey);
} else {
@@ -322,14 +301,14 @@ public class PgpSignEncrypt {
encryptionOut = cPk.open(out, new byte[1 << 16]);
if (enableCompression) {
- compressGen = new PGPCompressedDataGenerator(compressionId);
+ compressGen = new PGPCompressedDataGenerator(mCompressionId);
bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
} else {
bcpgOut = new BCPGOutputStream(encryptionOut);
}
if (enableSignature) {
- if (signatureForceV3) {
+ if (mSignatureForceV3) {
signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut);
} else {
signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
@@ -345,13 +324,13 @@ public class PgpSignEncrypt {
long progress = 0;
int n;
byte[] buffer = new byte[1 << 16];
- InputStream in = data.getInputStream();
+ InputStream in = mData.getInputStream();
while ((n = in.read(buffer)) > 0) {
pOut.write(buffer, 0, n);
// update signature buffer if signature is requested
if (enableSignature) {
- if (signatureForceV3) {
+ if (mSignatureForceV3) {
signatureV3Generator.update(buffer, 0, n);
} else {
signatureGenerator.update(buffer, 0, n);
@@ -359,26 +338,26 @@ public class PgpSignEncrypt {
}
progress += n;
- if (data.getSize() != 0) {
- updateProgress((int) (20 + (95 - 20) * progress / data.getSize()), 100);
+ if (mData.getSize() != 0) {
+ updateProgress((int) (20 + (95 - 20) * progress / mData.getSize()), 100);
}
}
literalGen.close();
- } else if (enableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
+ } else if (mEnableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
/* sign-only of ascii text */
updateProgress(R.string.progress_signing, 40, 100);
// write directly on armor output stream
- armorOut.beginClearText(signatureHashAlgorithm);
+ armorOut.beginClearText(mSignatureHashAlgorithm);
- InputStream in = data.getInputStream();
+ InputStream in = mData.getInputStream();
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
final byte[] newline = "\r\n".getBytes("UTF-8");
- if (signatureForceV3) {
+ if (mSignatureForceV3) {
processLine(reader.readLine(), armorOut, signatureV3Generator);
} else {
processLine(reader.readLine(), armorOut, signatureGenerator);
@@ -395,7 +374,7 @@ public class PgpSignEncrypt {
armorOut.write(newline);
// update signature buffer with input line
- if (signatureForceV3) {
+ if (mSignatureForceV3) {
signatureV3Generator.update(newline);
processLine(line, armorOut, signatureV3Generator);
} else {
@@ -415,7 +394,7 @@ public class PgpSignEncrypt {
if (enableSignature) {
updateProgress(R.string.progress_generating_signature, 95, 100);
- if (signatureForceV3) {
+ if (mSignatureForceV3) {
signatureV3Generator.generate().encode(pOut);
} else {
signatureGenerator.generate().encode(pOut);
@@ -432,12 +411,12 @@ public class PgpSignEncrypt {
encryptionOut.close();
}
- if (enableAsciiArmorOutput) {
+ if (mEnableAsciiArmorOutput) {
armorOut.close();
}
out.close();
- outStream.close();
+ mOutStream.close();
updateProgress(R.string.progress_done, 100, 100);
}
@@ -449,35 +428,36 @@ public class PgpSignEncrypt {
SignatureException {
OutputStream out;
- if (enableAsciiArmorOutput) {
+ if (mEnableAsciiArmorOutput) {
// Ascii Armor (Radix-64)
- ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
- armorOut.setHeader("Version", PgpHelper.getFullVersion(context));
+ ArmoredOutputStream armorOut = new ArmoredOutputStream(mOutStream);
+ armorOut.setHeader("Version", PgpHelper.getFullVersion(mContext));
out = armorOut;
} else {
- out = outStream;
+ out = mOutStream;
}
- if (signatureKeyId == 0) {
- throw new PgpGeneralException(context.getString(R.string.error_no_signature_key));
+ if (mSignatureKeyId == 0) {
+ throw new PgpGeneralException(mContext.getString(R.string.error_no_signature_key));
}
- PGPSecretKeyRing signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId);
- PGPSecretKey signingKey = PgpKeyHelper.getSigningKey(context, signatureKeyId);
+ PGPSecretKeyRing signingKeyRing =
+ ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, mSignatureKeyId);
+ PGPSecretKey signingKey = PgpKeyHelper.getSigningKey(mContext, mSignatureKeyId);
if (signingKey == null) {
- throw new PgpGeneralException(context.getString(R.string.error_signature_failed));
+ throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
}
- if (signaturePassphrase == null) {
- throw new PgpGeneralException(context.getString(R.string.error_no_signature_passphrase));
+ if (mSignaturePassphrase == null) {
+ throw new PgpGeneralException(mContext.getString(R.string.error_no_signature_passphrase));
}
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray());
PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
if (signaturePrivateKey == null) {
throw new PgpGeneralException(
- context.getString(R.string.error_could_not_extract_private_key));
+ mContext.getString(R.string.error_could_not_extract_private_key));
}
updateProgress(R.string.progress_preparing_streams, 0, 100);
@@ -490,12 +470,12 @@ public class PgpSignEncrypt {
// content signer based on signing key algorithm and chosen hash algorithm
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey
- .getPublicKey().getAlgorithm(), signatureHashAlgorithm)
+ .getPublicKey().getAlgorithm(), mSignatureHashAlgorithm)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator signatureGenerator = null;
PGPV3SignatureGenerator signatureV3Generator = null;
- if (signatureForceV3) {
+ if (mSignatureForceV3) {
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
signatureV3Generator.init(type, signaturePrivateKey);
} else {
@@ -510,7 +490,7 @@ public class PgpSignEncrypt {
updateProgress(R.string.progress_signing, 40, 100);
- InputStream inStream = data.getInputStream();
+ InputStream inStream = mData.getInputStream();
// if (binary) {
// byte[] buffer = new byte[1 << 16];
// int n = 0;
@@ -527,7 +507,7 @@ public class PgpSignEncrypt {
String line;
while ((line = reader.readLine()) != null) {
- if (signatureForceV3) {
+ if (mSignatureForceV3) {
processLine(line, null, signatureV3Generator);
signatureV3Generator.update(newline);
} else {
@@ -538,13 +518,13 @@ public class PgpSignEncrypt {
// }
BCPGOutputStream bOut = new BCPGOutputStream(out);
- if (signatureForceV3) {
+ if (mSignatureForceV3) {
signatureV3Generator.generate().encode(bOut);
} else {
signatureGenerator.generate().encode(bOut);
}
out.close();
- outStream.close();
+ mOutStream.close();
updateProgress(R.string.progress_done, 100, 100);
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java
index e18eb0d6d..54601173d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java
@@ -1,33 +1,24 @@
-package org.sufficientlysecure.keychain.pgp;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.text.DateFormat;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.Vector;
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
+package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.asn1.DERObjectIdentifier;
-import org.spongycastle.asn1.x509.AuthorityKeyIdentifier;
-import org.spongycastle.asn1.x509.BasicConstraints;
-import org.spongycastle.asn1.x509.GeneralName;
-import org.spongycastle.asn1.x509.GeneralNames;
-import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
-import org.spongycastle.asn1.x509.X509Extensions;
-import org.spongycastle.asn1.x509.X509Name;
+import org.spongycastle.asn1.x509.*;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
@@ -38,30 +29,38 @@ import org.spongycastle.x509.extension.SubjectKeyIdentifierStructure;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Vector;
+
public class PgpToX509 {
- public final static String DN_COMMON_PART_O = "OpenPGP to X.509 Bridge";
- public final static String DN_COMMON_PART_OU = "OpenPGP Keychain cert";
+ public static final String DN_COMMON_PART_O = "OpenPGP to X.509 Bridge";
+ public static final String DN_COMMON_PART_OU = "OpenPGP Keychain cert";
/**
* Creates a self-signed certificate from a public and private key. The (critical) key-usage
* extension is set up with: digital signature, non-repudiation, key-encipherment, key-agreement
* and certificate-signing. The (non-critical) Netscape extension is set up with: SSL client and
* S/MIME. A URI subjectAltName may also be set up.
- *
- * @param pubKey
- * public key
- * @param privKey
- * private key
- * @param subject
- * subject (and issuer) DN for this certificate, RFC 2253 format preferred.
- * @param startDate
- * date from which the certificate will be valid (defaults to current date and time
- * if null)
- * @param endDate
- * date until which the certificate will be valid (defaults to current date and time
- * if null) *
- * @param subjAltNameURI
- * URI to be placed in subjectAltName
+ *
+ * @param pubKey public key
+ * @param privKey private key
+ * @param subject subject (and issuer) DN for this certificate, RFC 2253 format preferred.
+ * @param startDate date from which the certificate will be valid (defaults to current date and time
+ * if null)
+ * @param endDate date until which the certificate will be valid (defaults to current date and time
+ * if null) *
+ * @param subjAltNameURI URI to be placed in subjectAltName
* @return self-signed certificate
* @throws InvalidKeyException
* @throws SignatureException
@@ -70,11 +69,10 @@ public class PgpToX509 {
* @throws NoSuchProviderException
* @throws CertificateException
* @throws Exception
- *
* @author Bruno Harbulot
*/
public static X509Certificate createSelfSignedCert(PublicKey pubKey, PrivateKey privKey,
- X509Name subject, Date startDate, Date endDate, String subjAltNameURI)
+ X509Name subject, Date startDate, Date endDate, String subjAltNameURI)
throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException,
SignatureException, CertificateException, NoSuchProviderException {
@@ -171,15 +169,12 @@ public class PgpToX509 {
/**
* Creates a self-signed certificate from a PGP Secret Key.
- *
- * @param pgpSecKey
- * PGP Secret Key (from which one can extract the public and private keys and other
- * attributes).
- * @param pgpPrivKey
- * PGP Private Key corresponding to the Secret Key (password callbacks should be done
- * before calling this method)
- * @param subjAltNameURI
- * optional URI to embed in the subject alternative-name
+ *
+ * @param pgpSecKey PGP Secret Key (from which one can extract the public and private keys and other
+ * attributes).
+ * @param pgpPrivKey PGP Private Key corresponding to the Secret Key (password callbacks should be done
+ * before calling this method)
+ * @param subjAltNameURI optional URI to embed in the subject alternative-name
* @return self-signed certificate
* @throws PGPException
* @throws NoSuchProviderException
@@ -187,11 +182,10 @@ public class PgpToX509 {
* @throws NoSuchAlgorithmException
* @throws SignatureException
* @throws CertificateException
- *
* @author Bruno Harbulot
*/
public static X509Certificate createSelfSignedCert(PGPSecretKey pgpSecKey,
- PGPPrivateKey pgpPrivKey, String subjAltNameURI) throws PGPException,
+ PGPPrivateKey pgpPrivKey, String subjAltNameURI) throws PGPException,
NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException,
SignatureException, CertificateException {
// get public key from secret key
@@ -213,7 +207,7 @@ public class PgpToX509 {
x509NameValues.add(DN_COMMON_PART_OU);
for (@SuppressWarnings("unchecked")
- Iterator<Object> it = (Iterator<Object>) pgpSecKey.getUserIDs(); it.hasNext();) {
+ Iterator<Object> it = (Iterator<Object>) pgpSecKey.getUserIDs(); it.hasNext(); ) {
Object attrib = it.next();
x509NameOids.add(X509Name.CN);
x509NameValues.add("CryptoCall");
@@ -225,7 +219,7 @@ public class PgpToX509 {
*/
Log.d(Constants.TAG, "User attributes: ");
for (@SuppressWarnings("unchecked")
- Iterator<Object> it = (Iterator<Object>) pgpSecKey.getUserAttributes(); it.hasNext();) {
+ Iterator<Object> it = (Iterator<Object>) pgpSecKey.getUserAttributes(); it.hasNext(); ) {
Object attrib = it.next();
Log.d(Constants.TAG, " - " + attrib + " -- " + attrib.getClass());
}
@@ -261,14 +255,13 @@ public class PgpToX509 {
* This is a password callback handler that will fill in a password automatically. Useful to
* configure passwords in advance, but should be used with caution depending on how much you
* allow passwords to be stored within your application.
- *
+ *
* @author Bruno Harbulot.
- *
*/
- public final static class PredefinedPasswordCallbackHandler implements CallbackHandler {
+ public static final class PredefinedPasswordCallbackHandler implements CallbackHandler {
- private char[] password;
- private String prompt;
+ private char[] mPassword;
+ private String mPrompt;
public PredefinedPasswordCallbackHandler(String password) {
this(password == null ? null : password.toCharArray(), null);
@@ -283,16 +276,16 @@ public class PgpToX509 {
}
public PredefinedPasswordCallbackHandler(char[] password, String prompt) {
- this.password = password;
- this.prompt = prompt;
+ this.mPassword = password;
+ this.mPrompt = prompt;
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof PasswordCallback) {
PasswordCallback pwCallback = (PasswordCallback) callback;
- if ((this.prompt == null) || (this.prompt.equals(pwCallback.getPrompt()))) {
- pwCallback.setPassword(this.password);
+ if ((this.mPrompt == null) || (this.mPrompt.equals(pwCallback.getPrompt()))) {
+ pwCallback.setPassword(this.mPassword);
}
} else {
throw new UnsupportedCallbackException(callback, "Unrecognised callback.");
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/NoAsymmetricEncryptionException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/NoAsymmetricEncryptionException.java
index 92542fa35..23c4bbbd9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/NoAsymmetricEncryptionException.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/NoAsymmetricEncryptionException.java
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package org.sufficientlysecure.keychain.pgp.exception;
public class NoAsymmetricEncryptionException extends Exception {
@@ -6,4 +23,4 @@ public class NoAsymmetricEncryptionException extends Exception {
public NoAsymmetricEncryptionException() {
super();
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
index 36c663727..bb80d27ee 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package org.sufficientlysecure.keychain.pgp.exception;
public class PgpGeneralException extends Exception {
@@ -6,4 +23,4 @@ public class PgpGeneralException extends Exception {
public PgpGeneralException(String message) {
super(message);
}
-} \ No newline at end of file
+}
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..e7b31bf65 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
@@ -17,11 +17,11 @@
package org.sufficientlysecure.keychain.provider;
-import org.sufficientlysecure.keychain.Constants;
-
import android.net.Uri;
import android.provider.BaseColumns;
+import org.sufficientlysecure.keychain.Constants;
+
public class KeychainContract {
interface KeyRingsColumns {
@@ -57,10 +57,15 @@ public class KeychainContract {
interface ApiAppsColumns {
String PACKAGE_NAME = "package_name";
String PACKAGE_SIGNATURE = "package_signature";
+ }
+
+ interface ApiAppsAccountsColumns {
+ String ACCOUNT_NAME = "account_name";
String KEY_ID = "key_id"; // not a database id
String ENCRYPTION_ALGORITHM = "encryption_algorithm";
String HASH_ALORITHM = "hash_algorithm";
String COMPRESSION = "compression";
+ String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name
}
public static final class KeyTypes {
@@ -88,18 +93,26 @@ public class KeychainContract {
public static final String PATH_KEYS = "keys";
public static final String BASE_API_APPS = "api_apps";
- public static final String PATH_BY_PACKAGE_NAME = "package_name";
+ public static final String PATH_ACCOUNTS = "accounts";
public static class KeyRings implements KeyRingsColumns, BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
- /** Use if multiple items get returned */
+ /**
+ * Use if multiple items get returned
+ */
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring";
- /** Use if a single item is returned */
+ /**
+ * Use if a single item is returned
+ */
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring";
+ public static Uri buildUnifiedKeyRingsUri() {
+ return CONTENT_URI;
+ }
+
public static Uri buildPublicKeyRingsUri() {
return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
}
@@ -147,6 +160,7 @@ public class KeychainContract {
}
public static Uri buildSecretKeyRingsByEmailsUri(String emails) {
+ // TODO: encoded?
return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS)
.appendPath(emails).build();
}
@@ -161,10 +175,14 @@ public class KeychainContract {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
- /** Use if multiple items get returned */
+ /**
+ * Use if multiple items get returned
+ */
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key";
- /** Use if a single item is returned */
+ /**
+ * Use if a single item is returned
+ */
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key";
public static Uri buildPublicKeysUri(String keyRingRowId) {
@@ -200,10 +218,14 @@ public class KeychainContract {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
- /** Use if multiple items get returned */
+ /**
+ * Use if multiple items get returned
+ */
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
- /** Use if a single item is returned */
+ /**
+ * Use if a single item is returned
+ */
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
public static Uri buildPublicUserIdsUri(String keyRingRowId) {
@@ -239,20 +261,44 @@ public class KeychainContract {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_API_APPS).build();
- /** Use if multiple items get returned */
+ /**
+ * Use if multiple items get returned
+ */
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps";
- /** Use if a single item is returned */
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_apps";
+ /**
+ * Use if a single item is returned
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_app";
- public static Uri buildIdUri(String rowId) {
- return CONTENT_URI.buildUpon().appendPath(rowId).build();
+ public static Uri buildByPackageNameUri(String packageName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build();
}
+ }
- public static Uri buildByPackageNameUri(String packageName) {
- return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName)
+ public static class ApiAccounts implements ApiAppsAccountsColumns, BaseColumns {
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
+ .appendPath(BASE_API_APPS).build();
+
+ /**
+ * Use if multiple items get returned
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_app.accounts";
+
+ /**
+ * Use if a single item is returned
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_app.account";
+
+ public static Uri buildBaseUri(String packageName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS)
.build();
}
+
+ public static Uri buildByPackageAndAccountUri(String packageName, String accountName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS)
+ .appendEncodedPath(accountName).build();
+ }
}
public static class DataStream {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 5f18ed6f9..8c33844b2 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -17,27 +17,29 @@
package org.sufficientlysecure.keychain.provider;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.provider.BaseColumns;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
import org.sufficientlysecure.keychain.util.Log;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.provider.BaseColumns;
-
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "apg.db";
- private static final int DATABASE_VERSION = 7;
+ private static final int DATABASE_VERSION = 8;
public interface Tables {
String KEY_RINGS = "key_rings";
String KEYS = "keys";
String USER_IDS = "user_ids";
String API_APPS = "api_apps";
+ String API_ACCOUNTS = "api_accounts";
}
private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS
@@ -62,26 +64,35 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ KeysColumns.KEY_DATA + " BLOB,"
+ KeysColumns.RANK + " INTEGER, "
+ KeysColumns.FINGERPRINT + " BLOB, "
- + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
- + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
- + BaseColumns._ID + ") ON DELETE CASCADE)";
+ + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, "
+ + "FOREIGN KEY(" + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES "
+ + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)";
private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ UserIdsColumns.USER_ID + " TEXT, "
+ UserIdsColumns.RANK + " INTEGER, "
- + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
- + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
- + BaseColumns._ID + ") ON DELETE CASCADE)";
+ + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, "
+ + "FOREIGN KEY(" + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES "
+ + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)";
private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, "
- + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB, "
- + ApiAppsColumns.KEY_ID + " INT64, "
- + ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
- + ApiAppsColumns.HASH_ALORITHM + " INTEGER, "
- + ApiAppsColumns.COMPRESSION + " INTEGER)";
+ + ApiAppsColumns.PACKAGE_NAME + " TEXT NOT NULL UNIQUE, "
+ + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB)";
+
+ private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ACCOUNTS
+ + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + ApiAppsAccountsColumns.ACCOUNT_NAME + " TEXT NOT NULL, "
+ + ApiAppsAccountsColumns.KEY_ID + " INT64, "
+ + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
+ + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, "
+ + ApiAppsAccountsColumns.COMPRESSION + " INTEGER, "
+ + ApiAppsAccountsColumns.PACKAGE_NAME + " TEXT NOT NULL, "
+ + "UNIQUE(" + ApiAppsAccountsColumns.ACCOUNT_NAME + ", "
+ + ApiAppsAccountsColumns.PACKAGE_NAME + "), "
+ + "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME + ") REFERENCES "
+ + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)";
KeychainDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -95,6 +106,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL(CREATE_KEYS);
db.execSQL(CREATE_USER_IDS);
db.execSQL(CREATE_API_APPS);
+ db.execSQL(CREATE_API_APPS_ACCOUNTS);
}
@Override
@@ -134,6 +146,12 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT
+ " BLOB;");
break;
+ case 7:
+ // new db layout for api apps
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
+ db.execSQL(CREATE_API_APPS);
+ db.execSQL(CREATE_API_APPS_ACCOUNTS);
+ break;
default:
break;
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 781f36758..1c5e3ab36 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,10 +17,20 @@
package org.sufficientlysecure.keychain.provider;
-import java.util.Arrays;
-import java.util.HashMap;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
@@ -32,17 +42,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log;
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteConstraintException;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.provider.BaseColumns;
-import android.text.TextUtils;
+import java.util.Arrays;
+import java.util.HashMap;
public class KeychainProvider extends ContentProvider {
// public static final String ACTION_BROADCAST_DATABASE_CHANGE = Constants.PACKAGE_NAME
@@ -63,6 +64,7 @@ public class KeychainProvider extends ContentProvider {
private static final int PUBLIC_KEY_RING_USER_ID = 121;
private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122;
+ private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID = 123;
private static final int SECRET_KEY_RING = 201;
private static final int SECRET_KEY_RING_BY_ROW_ID = 202;
@@ -78,8 +80,11 @@ public class KeychainProvider extends ContentProvider {
private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222;
private static final int API_APPS = 301;
- private static final int API_APPS_BY_ROW_ID = 302;
private static final int API_APPS_BY_PACKAGE_NAME = 303;
+ private static final int API_ACCOUNTS = 304;
+ private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306;
+
+ private static final int UNIFIED_KEY_RING = 401;
// private static final int DATA_STREAM = 401;
@@ -95,6 +100,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>
@@ -147,6 +161,7 @@ public class KeychainProvider extends ContentProvider {
* <pre>
* key_rings/public/#/user_ids
* key_rings/public/#/user_ids/#
+ * key_rings/public/master_key_id/#/user_ids
* </pre>
*/
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
@@ -155,6 +170,10 @@ public class KeychainProvider extends ContentProvider {
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
+ KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS + "/#",
PUBLIC_KEY_RING_USER_ID_BY_ROW_ID);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
+ + KeychainContract.PATH_PUBLIC + "/"
+ + KeychainContract.PATH_BY_MASTER_KEY_ID + "/*/" + KeychainContract.PATH_USER_IDS,
+ PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID);
/**
* secret key rings
@@ -220,11 +239,22 @@ public class KeychainProvider extends ContentProvider {
/**
* API apps
+ *
+ * <pre>
+ * api_apps
+ * api_apps/_ (package name)
+ *
+ * api_apps/_/accounts
+ * api_apps/_/accounts/_ (account name)
+ * </pre>
*/
matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS);
- matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/"
- + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME);
+
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ + KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ + KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME);
/**
* data stream
@@ -238,7 +268,7 @@ public class KeychainProvider extends ContentProvider {
return matcher;
}
- private KeychainDatabase mApgDatabase;
+ private KeychainDatabase mKeychainDatabase;
/**
* {@inheritDoc}
@@ -246,7 +276,7 @@ public class KeychainProvider extends ContentProvider {
@Override
public boolean onCreate() {
mUriMatcher = buildUriMatcher();
- mApgDatabase = new KeychainDatabase(getContext());
+ mKeychainDatabase = new KeychainDatabase(getContext());
return true;
}
@@ -282,6 +312,7 @@ public class KeychainProvider extends ContentProvider {
return Keys.CONTENT_ITEM_TYPE;
case PUBLIC_KEY_RING_USER_ID:
+ case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
case SECRET_KEY_RING_USER_ID:
return UserIds.CONTENT_TYPE;
@@ -292,10 +323,15 @@ public class KeychainProvider extends ContentProvider {
case API_APPS:
return ApiApps.CONTENT_TYPE;
- case API_APPS_BY_ROW_ID:
case API_APPS_BY_PACKAGE_NAME:
return ApiApps.CONTENT_ITEM_TYPE;
+ case API_ACCOUNTS:
+ return ApiAccounts.CONTENT_TYPE;
+
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ return ApiAccounts.CONTENT_ITEM_TYPE;
+
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
@@ -304,7 +340,7 @@ public class KeychainProvider extends ContentProvider {
/**
* Returns type of the query (secret/public)
*
- * @param uri
+ * @param match
* @return
*/
private int getKeyType(int match) {
@@ -319,6 +355,7 @@ public class KeychainProvider extends ContentProvider {
case PUBLIC_KEY_RING_KEY:
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case PUBLIC_KEY_RING_USER_ID:
+ case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
type = KeyTypes.PUBLIC;
break;
@@ -356,15 +393,25 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
+ KeyRingsColumns.KEY_RING_DATA);
- projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID);
+ projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
+ + KeyRingsColumns.MASTER_KEY_ID);
// TODO: deprecated master key id
//projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEYS + "." + KeysColumns.KEY_ID);
+ projectionMap.put(KeysColumns.ALGORITHM, Tables.KEYS + "." + KeysColumns.ALGORITHM);
+ projectionMap.put(KeysColumns.KEY_SIZE, Tables.KEYS + "." + KeysColumns.KEY_SIZE);
+ projectionMap.put(KeysColumns.CREATION, Tables.KEYS + "." + KeysColumns.CREATION);
+ projectionMap.put(KeysColumns.EXPIRY, Tables.KEYS + "." + KeysColumns.EXPIRY);
+ projectionMap.put(KeysColumns.KEY_RING_ROW_ID, Tables.KEYS + "." + KeysColumns.KEY_RING_ROW_ID);
projectionMap.put(KeysColumns.FINGERPRINT, Tables.KEYS + "." + KeysColumns.FINGERPRINT);
projectionMap.put(KeysColumns.IS_REVOKED, Tables.KEYS + "." + KeysColumns.IS_REVOKED);
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;
}
@@ -395,13 +442,27 @@ public class KeychainProvider extends ContentProvider {
return projectionMap;
}
+ private HashMap<String, String> getProjectionMapForUserIds() {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+
+ projectionMap.put(BaseColumns._ID, Tables.USER_IDS + "." + BaseColumns._ID);
+ projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
+ projectionMap.put(UserIdsColumns.RANK, Tables.USER_IDS + "." + UserIdsColumns.RANK);
+ projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
+ + KeyRingsColumns.MASTER_KEY_ID);
+
+ return projectionMap;
+ }
+
/**
* 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)
@@ -451,11 +512,26 @@ public class KeychainProvider extends ContentProvider {
Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- SQLiteDatabase db = mApgDatabase.getReadableDatabase();
+ SQLiteDatabase db = mKeychainDatabase.getReadableDatabase();
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);
@@ -465,7 +541,6 @@ public class KeychainProvider extends ContentProvider {
}
break;
-
case PUBLIC_KEY_RING_BY_ROW_ID:
case SECRET_KEY_RING_BY_ROW_ID:
qb = buildKeyRingQuery(qb, match);
@@ -478,7 +553,6 @@ public class KeychainProvider extends ContentProvider {
}
break;
-
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
qb = buildKeyRingQuery(qb, match);
@@ -491,7 +565,6 @@ public class KeychainProvider extends ContentProvider {
}
break;
-
case SECRET_KEY_RING_BY_KEY_ID:
case PUBLIC_KEY_RING_BY_KEY_ID:
qb = buildKeyRingQueryWithSpecificKey(qb, match);
@@ -504,7 +577,6 @@ public class KeychainProvider extends ContentProvider {
}
break;
-
case SECRET_KEY_RING_BY_EMAILS:
case PUBLIC_KEY_RING_BY_EMAILS:
qb = buildKeyRingQuery(qb, match);
@@ -534,7 +606,6 @@ public class KeychainProvider extends ContentProvider {
}
break;
-
case SECRET_KEY_RING_BY_LIKE_EMAIL:
case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
qb = buildKeyRingQuery(qb, match);
@@ -550,7 +621,6 @@ public class KeychainProvider extends ContentProvider {
+ "))");
break;
-
case PUBLIC_KEY_RING_KEY:
case SECRET_KEY_RING_KEY:
qb.setTables(Tables.KEYS);
@@ -563,7 +633,6 @@ public class KeychainProvider extends ContentProvider {
qb.setProjectionMap(getProjectionMapForKeys());
break;
-
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case SECRET_KEY_RING_KEY_BY_ROW_ID:
qb.setTables(Tables.KEYS);
@@ -579,7 +648,16 @@ public class KeychainProvider extends ContentProvider {
qb.setProjectionMap(getProjectionMapForKeys());
break;
+ case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
+ qb.setTables(Tables.USER_IDS + " INNER JOIN " + Tables.KEY_RINGS + " ON " + "("
+ + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "."
+ + KeysColumns.KEY_RING_ROW_ID + " )");
+ qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(3));
+
+ qb.setProjectionMap(getProjectionMapForUserIds());
+ break;
case PUBLIC_KEY_RING_USER_ID:
case SECRET_KEY_RING_USER_ID:
qb.setTables(Tables.USER_IDS);
@@ -587,7 +665,6 @@ public class KeychainProvider extends ContentProvider {
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
break;
-
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
qb.setTables(Tables.USER_IDS);
@@ -598,25 +675,31 @@ public class KeychainProvider extends ContentProvider {
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
-
case API_APPS:
qb.setTables(Tables.API_APPS);
break;
- case API_APPS_BY_ROW_ID:
+ case API_APPS_BY_PACKAGE_NAME:
qb.setTables(Tables.API_APPS);
-
- qb.appendWhere(BaseColumns._ID + " = ");
+ qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
- case API_APPS_BY_PACKAGE_NAME:
- qb.setTables(Tables.API_APPS);
- qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ case API_ACCOUNTS:
+ qb.setTables(Tables.API_ACCOUNTS);
+ qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ qb.setTables(Tables.API_ACCOUNTS);
+ qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+ qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + ApiAccounts.ACCOUNT_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
+
+ break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
@@ -630,7 +713,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);
@@ -653,7 +736,7 @@ public class KeychainProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+ final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase();
Uri rowUri = null;
long rowId = -1;
@@ -673,12 +756,14 @@ public class KeychainProvider extends ContentProvider {
values.put(Keys.TYPE, KeyTypes.PUBLIC);
rowId = db.insertOrThrow(Tables.KEYS, null, values);
+ // TODO: this is wrong:
rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case PUBLIC_KEY_RING_USER_ID:
rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
+ // TODO: this is wrong:
rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
@@ -695,18 +780,33 @@ public class KeychainProvider extends ContentProvider {
values.put(Keys.TYPE, KeyTypes.SECRET);
rowId = db.insertOrThrow(Tables.KEYS, null, values);
+ // TODO: this is wrong:
rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
break;
case SECRET_KEY_RING_USER_ID:
rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
+ // TODO: this is wrong:
rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));
break;
case API_APPS:
rowId = db.insertOrThrow(Tables.API_APPS, null, values);
- rowUri = ApiApps.buildIdUri(Long.toString(rowId));
+// rowUri = ApiApps.buildIdUri(Long.toString(rowId));
+
+ break;
+ case API_ACCOUNTS:
+ // set foreign key automatically based on given uri
+ // e.g., api_apps/com.example.app/accounts/
+ String packageName = uri.getPathSegments().get(1);
+ values.put(ApiAccounts.PACKAGE_NAME, packageName);
+
+ Log.d(Constants.TAG, "provider packageName: " + packageName);
+
+ rowId = db.insertOrThrow(Tables.API_ACCOUNTS, null, values);
+ // TODO: this is wrong:
+// rowUri = ApiAccounts.buildIdUri(Long.toString(rowId));
break;
default:
@@ -730,7 +830,7 @@ public class KeychainProvider extends ContentProvider {
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.v(Constants.TAG, "delete(uri=" + uri + ")");
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+ final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase();
int count;
final int match = mUriMatcher.match(uri);
@@ -766,12 +866,12 @@ public class KeychainProvider extends ContentProvider {
count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
selectionArgs);
break;
- case API_APPS_BY_ROW_ID:
- count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection),
+ case API_APPS_BY_PACKAGE_NAME:
+ count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, selection),
selectionArgs);
break;
- case API_APPS_BY_PACKAGE_NAME:
- count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection),
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, selection),
selectionArgs);
break;
default:
@@ -791,7 +891,7 @@ public class KeychainProvider extends ContentProvider {
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+ final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase();
String defaultSelection = null;
int count = 0;
@@ -836,13 +936,13 @@ public class KeychainProvider extends ContentProvider {
count = db.update(Tables.USER_IDS, values,
buildDefaultUserIdsSelection(uri, selection), selectionArgs);
break;
- case API_APPS_BY_ROW_ID:
- count = db.update(Tables.API_APPS, values,
- buildDefaultApiAppsSelection(uri, false, selection), selectionArgs);
- break;
case API_APPS_BY_PACKAGE_NAME:
count = db.update(Tables.API_APPS, values,
- buildDefaultApiAppsSelection(uri, true, selection), selectionArgs);
+ buildDefaultApiAppsSelection(uri, selection), selectionArgs);
+ break;
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ count = db.update(Tables.API_ACCOUNTS, values,
+ buildDefaultApiAccountsSelection(uri, selection), selectionArgs);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
@@ -862,7 +962,8 @@ public class KeychainProvider extends ContentProvider {
* Build default selection statement for KeyRings. If no extra selection is specified only build
* where clause with rowId
*
- * @param uri
+ * @param defaultSelection
+ * @param keyType
* @param selection
* @return
*/
@@ -940,19 +1041,29 @@ public class KeychainProvider extends ContentProvider {
* @param selection
* @return
*/
- private String buildDefaultApiAppsSelection(Uri uri, boolean packageSelection, String selection) {
- String lastPathSegment = uri.getLastPathSegment();
+ private String buildDefaultApiAppsSelection(Uri uri, String selection) {
+ String packageName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment());
String andSelection = "";
if (!TextUtils.isEmpty(selection)) {
andSelection = " AND (" + selection + ")";
}
- if (packageSelection) {
- return ApiApps.PACKAGE_NAME + "=" + lastPathSegment + andSelection;
- } else {
- return BaseColumns._ID + "=" + lastPathSegment + andSelection;
+ return ApiApps.PACKAGE_NAME + "=" + packageName + andSelection;
+ }
+
+ private String buildDefaultApiAccountsSelection(Uri uri, String selection) {
+ String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1));
+ String accountName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment());
+
+ String andSelection = "";
+ if (!TextUtils.isEmpty(selection)) {
+ andSelection = " AND (" + selection + ")";
}
+
+ return ApiAccounts.PACKAGE_NAME + "=" + packageName + " AND "
+ + ApiAccounts.ACCOUNT_NAME + "=" + accountName
+ + andSelection;
}
// @Override
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java
index a879d60a8..701ffc6af 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java
@@ -17,10 +17,9 @@
package org.sufficientlysecure.keychain.provider;
-import org.sufficientlysecure.keychain.Constants;
-
import android.net.Uri;
import android.provider.BaseColumns;
+import org.sufficientlysecure.keychain.Constants;
public class KeychainServiceBlobContract {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java
index fcee76fd7..da1bcb2d9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
-
import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns;
public class KeychainServiceBlobDatabase extends SQLiteOpenHelper {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java
index 5693e6de2..aa30e845d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java
@@ -18,11 +18,6 @@
package org.sufficientlysecure.keychain.provider;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.Blobs;
-import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -31,7 +26,10 @@ import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.BaseColumns;
-
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.Blobs;
+import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns;
+import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
@@ -40,7 +38,7 @@ import java.util.List;
import java.util.UUID;
public class KeychainServiceBlobProvider extends ContentProvider {
- private static final String STORE_PATH = Constants.path.APP_DIR + "/ApgBlobs";
+ private static final String STORE_PATH = Constants.Path.APP_DIR + "/KeychainBlobs";
private KeychainServiceBlobDatabase mBlobDatabase = null;
@@ -55,7 +53,9 @@ public class KeychainServiceBlobProvider extends ContentProvider {
return true;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public Uri insert(Uri uri, ContentValues ignored) {
// ContentValues are actually ignored, because we want to store a blob with no more
@@ -74,7 +74,9 @@ public class KeychainServiceBlobProvider extends ContentProvider {
return Uri.withAppendedPath(insertedUri, password);
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException,
FileNotFoundException {
@@ -91,9 +93,9 @@ public class KeychainServiceBlobProvider extends ContentProvider {
// get the data
SQLiteDatabase db = mBlobDatabase.getReadableDatabase();
- Cursor result = db.query(KeychainServiceBlobDatabase.TABLE, new String[] { BaseColumns._ID },
+ Cursor result = db.query(KeychainServiceBlobDatabase.TABLE, new String[]{BaseColumns._ID},
BaseColumns._ID + " = ? and " + BlobsColumns.KEY + " = ?",
- new String[] { id, key }, null, null, null);
+ new String[]{id, key}, null, null, null);
if (result.getCount() == 0) {
// either the key is wrong or no id exists
@@ -124,26 +126,34 @@ public class KeychainServiceBlobProvider extends ContentProvider {
return null;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public String getType(Uri uri) {
return null;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
+ String sortOrder) {
return null;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
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 fef3d391b..b47c4946f 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
@@ -17,10 +17,11 @@
package org.sufficientlysecure.keychain.provider;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Date;
+import android.content.*;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
+import android.os.RemoteException;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.openpgp.PGPKeyRing;
@@ -38,19 +39,15 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
-import org.sufficientlysecure.keychain.service.remote.AppSettings;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.net.Uri;
-import android.os.RemoteException;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
public class ProviderHelper {
@@ -87,7 +84,7 @@ public class ProviderHelper {
}
/**
- * Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId
+ * Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
*/
public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context,
long masterKeyId) {
@@ -110,11 +107,8 @@ public class ProviderHelper {
*/
public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) {
PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId);
- if (keyRing == null) {
- return null;
- }
- return keyRing.getPublicKey(keyId);
+ return (keyRing == null) ? null : keyRing.getPublicKey(keyId);
}
/**
@@ -149,11 +143,8 @@ public class ProviderHelper {
*/
public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) {
PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId);
- if (keyRing == null) {
- return null;
- }
- return keyRing.getSecretKey(keyId);
+ return (keyRing == null) ? null : keyRing.getSecretKey(keyId);
}
/**
@@ -168,7 +159,8 @@ public class ProviderHelper {
// get current _ID of key
long currentRowId = -1;
- Cursor oldQuery = context.getContentResolver().query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
+ Cursor oldQuery = context.getContentResolver()
+ .query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
if (oldQuery != null && oldQuery.moveToFirst()) {
currentRowId = oldQuery.getLong(0);
} else {
@@ -184,10 +176,12 @@ public class ProviderHelper {
ContentValues values = new ContentValues();
// use exactly the same _ID again to replace key in-place.
- // NOTE: If we would not use the same _ID again, getting back to the ViewKeyActivity would result in Nullpointer,
+ // NOTE: If we would not use the same _ID again,
+ // getting back to the ViewKeyActivity would result in Nullpointer,
// because the currently loaded key would be gone from the database
- if (currentRowId != -1)
+ if (currentRowId != -1) {
values.put(KeyRings._ID, currentRowId);
+ }
values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
@@ -211,8 +205,11 @@ public class ProviderHelper {
++userIdRank;
}
- for (PGPSignature certification : new IterableIterator<PGPSignature>(masterKey.getSignaturesOfType(PGPSignature.POSITIVE_CERTIFICATION))) {
- //TODO: how to do this?? we need to verify the signatures again and again when they are displayed...
+ for (PGPSignature certification :
+ new IterableIterator<PGPSignature>(
+ masterKey.getSignaturesOfType(PGPSignature.POSITIVE_CERTIFICATION))) {
+ // TODO: how to do this??
+ // we need to verify the signatures again and again when they are displayed...
// if (certification.verify
// operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank));
}
@@ -239,7 +236,8 @@ public class ProviderHelper {
// get current _ID of key
long currentRowId = -1;
- Cursor oldQuery = context.getContentResolver().query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
+ Cursor oldQuery = context.getContentResolver()
+ .query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
if (oldQuery != null && oldQuery.moveToFirst()) {
currentRowId = oldQuery.getLong(0);
} else {
@@ -255,10 +253,12 @@ public class ProviderHelper {
ContentValues values = new ContentValues();
// use exactly the same _ID again to replace key in-place.
- // NOTE: If we would not use the same _ID again, getting back to the ViewKeyActivity would result in Nullpointer,
+ // NOTE: If we would not use the same _ID again,
+ // getting back to the ViewKeyActivity would result in Nullpointer,
// because the currently loaded key would be gone from the database
- if (currentRowId != -1)
+ if (currentRowId != -1) {
values.put(KeyRings._ID, currentRowId);
+ }
values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
@@ -341,10 +341,10 @@ public class ProviderHelper {
long keyRingRowId, PGPSecretKey key, int rank) throws IOException {
ContentValues values = new ContentValues();
- boolean has_private = true;
+ boolean hasPrivate = true;
if (key.isMasterKey()) {
- if (PgpKeyHelper.isSecretKeyPrivateEmpty(key)) {
- has_private = false;
+ if (key.isPrivateKeyEmpty()) {
+ hasPrivate = false;
}
}
@@ -352,9 +352,9 @@ public class ProviderHelper {
values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
- values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && has_private));
- values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && has_private));
- values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key) && has_private);
+ values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && hasPrivate));
+ values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && hasPrivate));
+ values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key) && hasPrivate);
values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
@@ -411,6 +411,30 @@ public class ProviderHelper {
}
/**
+ * Private helper method
+ */
+ private static ArrayList<Long> getKeyRingsRowIds(Context context, Uri queryUri) {
+ Cursor cursor = context.getContentResolver().query(queryUri,
+ new String[]{KeyRings._ID}, null, null, null);
+
+ ArrayList<Long> rowIds = new ArrayList<Long>();
+ if (cursor != null) {
+ int idCol = cursor.getColumnIndex(KeyRings._ID);
+ if (cursor.moveToFirst()) {
+ do {
+ rowIds.add(cursor.getLong(idCol));
+ } while (cursor.moveToNext());
+ }
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ return rowIds;
+ }
+
+ /**
* Retrieves ids of all SecretKeyRings
*/
public static ArrayList<Long> getSecretKeyRingsMasterKeyIds(Context context) {
@@ -426,6 +450,22 @@ public class ProviderHelper {
return getKeyRingsMasterKeyIds(context, queryUri);
}
+ /**
+ * Retrieves ids of all SecretKeyRings
+ */
+ public static ArrayList<Long> getSecretKeyRingsRowIds(Context context) {
+ Uri queryUri = KeyRings.buildSecretKeyRingsUri();
+ return getKeyRingsRowIds(context, queryUri);
+ }
+
+ /**
+ * Retrieves ids of all PublicKeyRings
+ */
+ public static ArrayList<Long> getPublicKeyRingsRowIds(Context context) {
+ Uri queryUri = KeyRings.buildPublicKeyRingsUri();
+ return getKeyRingsRowIds(context, queryUri);
+ }
+
public static void deletePublicKeyRing(Context context, long rowId) {
ContentResolver cr = context.getContentResolver();
cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null);
@@ -436,6 +476,15 @@ public class ProviderHelper {
cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null);
}
+ public static void deleteUnifiedKeyRing(Context context, String masterKeyId, boolean isSecretKey) {
+ ContentResolver cr = context.getContentResolver();
+ cr.delete(KeyRings.buildPublicKeyRingsByMasterKeyIdUri(masterKeyId), null, null);
+ if (isSecretKey) {
+ cr.delete(KeyRings.buildSecretKeyRingsByMasterKeyIdUri(masterKeyId), null, null);
+ }
+
+ }
+
/**
* Get master key id of keyring by its row id
*/
@@ -449,13 +498,14 @@ public class ProviderHelper {
*/
public static boolean getSecretMasterKeyCanCertify(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyCanCertify(context, queryUri, keyRingRowId);
+ return getMasterKeyCanCertify(context, queryUri);
}
/**
* Private helper method to get master key private empty status of keyring by its row id
*/
- private static boolean getMasterKeyCanCertify(Context context, Uri queryUri, long keyRingRowId) {
+
+ public static boolean getMasterKeyCanCertify(Context context, Uri queryUri) {
String[] projection = new String[]{
KeyRings.MASTER_KEY_ID,
"(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS
@@ -481,6 +531,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
*/
@@ -512,6 +568,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
*/
@@ -733,19 +809,28 @@ public class ProviderHelper {
ContentValues values = new ContentValues();
values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
values.put(ApiApps.PACKAGE_SIGNATURE, appSettings.getPackageSignature());
- values.put(ApiApps.KEY_ID, appSettings.getKeyId());
- values.put(ApiApps.COMPRESSION, appSettings.getCompression());
- values.put(ApiApps.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm());
- values.put(ApiApps.HASH_ALORITHM, appSettings.getHashAlgorithm());
+ return values;
+ }
+ private static ContentValues contentValueForApiAccounts(AccountSettings accSettings) {
+ ContentValues values = new ContentValues();
+ values.put(KeychainContract.ApiAccounts.ACCOUNT_NAME, accSettings.getAccountName());
+ values.put(KeychainContract.ApiAccounts.KEY_ID, accSettings.getKeyId());
+ values.put(KeychainContract.ApiAccounts.COMPRESSION, accSettings.getCompression());
+ values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, accSettings.getEncryptionAlgorithm());
+ values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, accSettings.getHashAlgorithm());
return values;
}
public static void insertApiApp(Context context, AppSettings appSettings) {
- context.getContentResolver().insert(ApiApps.CONTENT_URI,
+ context.getContentResolver().insert(KeychainContract.ApiApps.CONTENT_URI,
contentValueForApiApps(appSettings));
}
+ public static void insertApiAccount(Context context, Uri uri, AccountSettings accSettings) {
+ context.getContentResolver().insert(uri, contentValueForApiAccounts(accSettings));
+ }
+
public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) {
if (context.getContentResolver().update(uri, contentValueForApiApps(appSettings), null,
null) <= 0) {
@@ -753,30 +838,59 @@ public class ProviderHelper {
}
}
+ public static void updateApiAccount(Context context, AccountSettings accSettings, Uri uri) {
+ if (context.getContentResolver().update(uri, contentValueForApiAccounts(accSettings), null,
+ null) <= 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Must be an uri pointing to an account
+ *
+ * @param context
+ * @param uri
+ * @return
+ */
public static AppSettings getApiAppSettings(Context context, Uri uri) {
AppSettings settings = null;
Cursor cur = context.getContentResolver().query(uri, null, null, null, null);
if (cur != null && cur.moveToFirst()) {
settings = new AppSettings();
- settings.setPackageName(cur.getString(cur
- .getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
- settings.setPackageSignature(cur.getBlob(cur
- .getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
- settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.ApiApps.KEY_ID)));
- settings.setCompression(cur.getInt(cur
- .getColumnIndexOrThrow(KeychainContract.ApiApps.COMPRESSION)));
- settings.setHashAlgorithm(cur.getInt(cur
- .getColumnIndexOrThrow(KeychainContract.ApiApps.HASH_ALORITHM)));
- settings.setEncryptionAlgorithm(cur.getInt(cur
- .getColumnIndexOrThrow(KeychainContract.ApiApps.ENCRYPTION_ALGORITHM)));
+ settings.setPackageName(cur.getString(
+ cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
+ settings.setPackageSignature(cur.getBlob(
+ cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
+ }
+
+ return settings;
+ }
+
+ public static AccountSettings getApiAccountSettings(Context context, Uri uri) {
+ AccountSettings settings = null;
+
+ Cursor cur = context.getContentResolver().query(uri, null, null, null, null);
+ if (cur != null && cur.moveToFirst()) {
+ settings = new AccountSettings();
+
+ settings.setAccountName(cur.getString(
+ cur.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
+ settings.setKeyId(cur.getLong(
+ cur.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
+ settings.setCompression(cur.getInt(
+ cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION)));
+ settings.setHashAlgorithm(cur.getInt(
+ cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM)));
+ settings.setEncryptionAlgorithm(cur.getInt(
+ cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM)));
}
return settings;
}
public static byte[] getApiAppSignature(Context context, String packageName) {
- Uri queryUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName);
+ Uri queryUri = ApiApps.buildByPackageNameUri(packageName);
String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE};
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java
index 9da4c8392..832cbc752 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java
@@ -15,80 +15,71 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Id;
-public class AppSettings {
- private String packageName;
- private byte[] packageSignature;
- private long keyId = Id.key.none;
- private int encryptionAlgorithm;
- private int hashAlgorithm;
- private int compression;
+public class AccountSettings {
+ private String mAccountName;
+ private long mKeyId = Id.key.none;
+ private int mEncryptionAlgorithm;
+ private int mHashAlgorithm;
+ private int mCompression;
- public AppSettings() {
+ public AccountSettings() {
}
- public AppSettings(String packageName, byte[] packageSignature) {
+ public AccountSettings(String accountName) {
super();
- this.packageName = packageName;
- this.packageSignature = packageSignature;
- // defaults:
- this.encryptionAlgorithm = PGPEncryptedData.AES_256;
- this.hashAlgorithm = HashAlgorithmTags.SHA512;
- this.compression = Id.choice.compression.zlib;
- }
+ this.mAccountName = accountName;
- public String getPackageName() {
- return packageName;
- }
-
- public void setPackageName(String packageName) {
- this.packageName = packageName;
+ // defaults:
+ this.mEncryptionAlgorithm = PGPEncryptedData.AES_256;
+ this.mHashAlgorithm = HashAlgorithmTags.SHA512;
+ this.mCompression = Id.choice.compression.zlib;
}
- public byte[] getPackageSignature() {
- return packageSignature;
+ public String getAccountName() {
+ return mAccountName;
}
- public void setPackageSignature(byte[] packageSignature) {
- this.packageSignature = packageSignature;
+ public void setAccountName(String mAccountName) {
+ this.mAccountName = mAccountName;
}
public long getKeyId() {
- return keyId;
+ return mKeyId;
}
public void setKeyId(long scretKeyId) {
- this.keyId = scretKeyId;
+ this.mKeyId = scretKeyId;
}
public int getEncryptionAlgorithm() {
- return encryptionAlgorithm;
+ return mEncryptionAlgorithm;
}
public void setEncryptionAlgorithm(int encryptionAlgorithm) {
- this.encryptionAlgorithm = encryptionAlgorithm;
+ this.mEncryptionAlgorithm = encryptionAlgorithm;
}
public int getHashAlgorithm() {
- return hashAlgorithm;
+ return mHashAlgorithm;
}
public void setHashAlgorithm(int hashAlgorithm) {
- this.hashAlgorithm = hashAlgorithm;
+ this.mHashAlgorithm = hashAlgorithm;
}
public int getCompression() {
- return compression;
+ return mCompression;
}
public void setCompression(int compression) {
- this.compression = compression;
+ this.mCompression = compression;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java
new file mode 100644
index 000000000..6c7e51bf0
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java
@@ -0,0 +1,54 @@
+/*
+ * 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.remote;
+
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.openpgp.PGPEncryptedData;
+import org.sufficientlysecure.keychain.Id;
+
+public class AppSettings {
+ private String mPackageName;
+ private byte[] mPackageSignature;
+
+ public AppSettings() {
+
+ }
+
+ public AppSettings(String packageName, byte[] packageSignature) {
+ super();
+ this.mPackageName = packageName;
+ this.mPackageSignature = packageSignature;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public void setPackageName(String packageName) {
+ this.mPackageName = packageName;
+ }
+
+ public byte[] getPackageSignature() {
+ return mPackageSignature;
+ }
+
+ public void setPackageSignature(byte[] packageSignature) {
+ this.mPackageSignature = packageSignature;
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index f697faa6e..ad0c658ad 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
import android.app.PendingIntent;
import android.content.Intent;
@@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -47,10 +48,6 @@ import java.util.ArrayList;
public class OpenPgpService extends RemoteService {
- private static final int PRIVATE_REQUEST_CODE_PASSPHRASE = 551;
- private static final int PRIVATE_REQUEST_CODE_USER_IDS = 552;
- private static final int PRIVATE_REQUEST_CODE_GET_KEYS = 553;
-
/**
* Search database for key ids based on emails.
*
@@ -100,7 +97,9 @@ public class OpenPgpService extends RemoteService {
intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds);
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_USER_IDS, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -126,7 +125,9 @@ public class OpenPgpService extends RemoteService {
intent.putExtra(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_PASSPHRASE, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -136,7 +137,7 @@ public class OpenPgpService extends RemoteService {
}
private Intent signImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AppSettings appSettings) {
+ ParcelFileDescriptor output, AccountSettings accSettings) {
try {
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
@@ -145,11 +146,11 @@ public class OpenPgpService extends RemoteService {
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
- passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
+ passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), accSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
+ Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
return passphraseBundle;
}
@@ -163,9 +164,9 @@ public class OpenPgpService extends RemoteService {
// sign-only
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
builder.enableAsciiArmorOutput(asciiArmor)
- .signatureHashAlgorithm(appSettings.getHashAlgorithm())
+ .signatureHashAlgorithm(accSettings.getHashAlgorithm())
.signatureForceV3(false)
- .signatureKeyId(appSettings.getKeyId())
+ .signatureKeyId(accSettings.getKeyId())
.signaturePassphrase(passphrase);
builder.build().execute();
} finally {
@@ -186,7 +187,7 @@ public class OpenPgpService extends RemoteService {
}
private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AppSettings appSettings, boolean sign) {
+ ParcelFileDescriptor output, AccountSettings accSettings, boolean sign) {
try {
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
@@ -208,14 +209,15 @@ public class OpenPgpService extends RemoteService {
} else {
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR,
- new OpenPgpError(OpenPgpError.GENERIC_ERROR, "Missing parameter user_ids or key_ids!"));
+ new OpenPgpError(OpenPgpError.GENERIC_ERROR,
+ "Missing parameter user_ids or key_ids!"));
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
}
// add own key for encryption
keyIds = Arrays.copyOf(keyIds, keyIds.length + 1);
- keyIds[keyIds.length - 1] = appSettings.getKeyId();
+ keyIds[keyIds.length - 1] = accSettings.getKeyId();
// build InputData and write into OutputStream
// Get Input- and OutputStream from ParcelFileDescriptor
@@ -227,8 +229,8 @@ public class OpenPgpService extends RemoteService {
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
builder.enableAsciiArmorOutput(asciiArmor)
- .compressionId(appSettings.getCompression())
- .symmetricEncryptionAlgorithm(appSettings.getEncryptionAlgorithm())
+ .compressionId(accSettings.getCompression())
+ .symmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm())
.encryptionKeyIds(keyIds);
if (sign) {
@@ -237,18 +239,18 @@ public class OpenPgpService extends RemoteService {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
- appSettings.getKeyId());
+ accSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
+ Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
return passphraseBundle;
}
// sign and encrypt
- builder.signatureHashAlgorithm(appSettings.getHashAlgorithm())
+ builder.signatureHashAlgorithm(accSettings.getHashAlgorithm())
.signatureForceV3(false)
- .signatureKeyId(appSettings.getKeyId())
+ .signatureKeyId(accSettings.getKeyId())
.signaturePassphrase(passphrase);
} else {
// encrypt only
@@ -274,7 +276,7 @@ public class OpenPgpService extends RemoteService {
}
private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AppSettings appSettings) {
+ ParcelFileDescriptor output, AccountSettings accSettings) {
try {
// Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
@@ -289,7 +291,8 @@ public class OpenPgpService extends RemoteService {
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
builder.assumeSymmetric(false) // no support for symmetric encryption
- .enforcedKeyId(appSettings.getKeyId()) // allow only the private key for this app for decryption
+ // allow only the private key for this app for decryption
+ .enforcedKeyId(accSettings.getKeyId())
.passphrase(passphrase);
// TODO: currently does not support binary signed-only content
@@ -297,7 +300,7 @@ public class OpenPgpService extends RemoteService {
if (decryptVerifyResult.isKeyPassphraseNeeded()) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
+ Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
return passphraseBundle;
} else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) {
throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
@@ -314,8 +317,9 @@ public class OpenPgpService extends RemoteService {
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
}
@@ -354,8 +358,9 @@ public class OpenPgpService extends RemoteService {
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
@@ -403,7 +408,8 @@ public class OpenPgpService extends RemoteService {
// version code is required and needs to correspond to version code of service!
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) {
Intent result = new Intent();
- OpenPgpError error = new OpenPgpError(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
+ OpenPgpError error = new OpenPgpError
+ (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
@@ -428,17 +434,26 @@ public class OpenPgpService extends RemoteService {
return errorResult;
}
- final AppSettings appSettings = getAppSettings();
+ String accName;
+ if (data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME) != null) {
+ accName = data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME);
+ } else {
+ accName = "default";
+ }
+ final AccountSettings accSettings = getAccSettings(accName);
+ if (accSettings == null) {
+ return getCreateAccountIntent(data, accName);
+ }
String action = data.getAction();
if (OpenPgpApi.ACTION_SIGN.equals(action)) {
- return signImpl(data, input, output, appSettings);
+ return signImpl(data, input, output, accSettings);
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, appSettings, false);
+ return encryptAndSignImpl(data, input, output, accSettings, false);
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, appSettings, true);
+ return encryptAndSignImpl(data, input, output, accSettings, true);
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
- return decryptAndVerifyImpl(data, input, output, appSettings);
+ return decryptAndVerifyImpl(data, input, output, accSettings);
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
return getKeyImpl(data);
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
index cb556be39..8cea3cd21 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
@@ -15,18 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import org.openintents.openpgp.OpenPgpError;
-import org.openintents.openpgp.util.OpenPgpApi;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
+package org.sufficientlysecure.keychain.remote;
import android.app.PendingIntent;
import android.app.Service;
@@ -39,16 +28,24 @@ import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
+import org.openintents.openpgp.OpenPgpError;
+import org.openintents.openpgp.util.OpenPgpApi;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
/**
* Abstract service class for remote APIs that handle app registration and user input.
*/
public abstract class RemoteService extends Service {
Context mContext;
- private static final int PRIVATE_REQUEST_CODE_REGISTER = 651;
- private static final int PRIVATE_REQUEST_CODE_ERROR = 652;
-
-
public Context getContext() {
return mContext;
}
@@ -56,13 +53,10 @@ public abstract class RemoteService extends Service {
protected Intent isAllowed(Intent data) {
try {
if (isCallerAllowed(false)) {
-
return null;
} else {
- String[] callingPackages = getPackageManager().getPackagesForUid(
- Binder.getCallingUid());
- // TODO: currently simply uses first entry
- String packageName = callingPackages[0];
+ String packageName = getCurrentCallingPackage();
+ Log.d(Constants.TAG, "isAllowed packageName: " + packageName);
byte[] packageSignature;
try {
@@ -84,7 +78,9 @@ public abstract class RemoteService extends Service {
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_REGISTER, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -98,10 +94,13 @@ public abstract class RemoteService extends Service {
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
- intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, getString(R.string.api_error_wrong_signature));
+ intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
+ getString(R.string.api_error_wrong_signature));
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_ERROR, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -123,26 +122,57 @@ public abstract class RemoteService extends Service {
}
/**
- * Retrieves AppSettings from database for the application calling this remote service
+ * Returns package name associated with the UID, which is assigned to the process that sent you the
+ * current transaction that is being processed :)
*
- * @return
+ * @return package name
*/
- protected AppSettings getAppSettings() {
+ private String getCurrentCallingPackage() {
+ // TODO:
+ // callingPackages contains more than one entry when sharedUserId has been used...
String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
+ String currentPkg = callingPackages[0];
+ Log.d(Constants.TAG, "currentPkg: " + currentPkg);
- // get app settings for this package
- for (int i = 0; i < callingPackages.length; i++) {
- String currentPkg = callingPackages[i];
+ return currentPkg;
+ }
+
+ /**
+ * Retrieves AccountSettings from database for the application calling this remote service
+ *
+ * @return
+ */
+ protected AccountSettings getAccSettings(String accountName) {
+ String currentPkg = getCurrentCallingPackage();
+ Log.d(Constants.TAG, "accountName: " + accountName);
- Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg);
+ Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName);
- AppSettings settings = ProviderHelper.getApiAppSettings(this, uri);
+ AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri);
- if (settings != null)
- return settings;
- }
+ return settings; // can be null!
+ }
+
+ protected Intent getCreateAccountIntent(Intent data, String accountName) {
+ String packageName = getCurrentCallingPackage();
+ Log.d(Constants.TAG, "accountName: " + accountName);
+
+ Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
+ intent.setAction(RemoteServiceActivity.ACTION_CREATE_ACCOUNT);
+ intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(RemoteServiceActivity.EXTRA_ACC_NAME, accountName);
+ intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
+
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+
+ // return PendingIntent to be executed by client
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
- return null;
+ return result;
}
/**
@@ -177,7 +207,7 @@ public abstract class RemoteService extends Service {
}
}
- Log.d(Constants.TAG, "Caller is NOT allowed!");
+ Log.d(Constants.TAG, "Uid is NOT allowed!");
return false;
}
@@ -189,7 +219,7 @@ public abstract class RemoteService extends Service {
* @throws WrongPackageSignatureException
*/
private boolean isPackageAllowed(String packageName) throws WrongPackageSignatureException {
- Log.d(Constants.TAG, "packageName: " + packageName);
+ Log.d(Constants.TAG, "isPackageAllowed packageName: " + packageName);
ArrayList<String> allowedPkgs = ProviderHelper.getRegisteredApiApps(this);
Log.d(Constants.TAG, "allowed: " + allowedPkgs);
@@ -217,6 +247,7 @@ public abstract class RemoteService extends Service {
}
}
+ Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName);
return false;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java
new file mode 100644
index 000000000..6f44a65e9
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.remote;
+
+public class WrongPackageSignatureException extends Exception {
+
+ private static final long serialVersionUID = -8294642703122196028L;
+
+ public WrongPackageSignatureException(String message) {
+ super(message);
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
index a7afc9698..671a3e0aa 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
@@ -15,13 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
+package org.sufficientlysecure.keychain.remote.ui;
import android.content.Intent;
import android.net.Uri;
@@ -31,17 +25,25 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-public class AppSettingsActivity extends ActionBarActivity {
- private Uri mAppUri;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ActionBarHelper;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class AccountSettingsActivity extends ActionBarActivity {
+ private Uri mAccountUri;
- private AppSettingsFragment mSettingsFragment;
+ private AccountSettingsFragment mAccountSettingsFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate a "Done" custom action bar
- ActionBarHelper.setDoneView(getSupportActionBar(), R.string.api_settings_save,
+ ActionBarHelper.setOneButtonView(getSupportActionBar(),
+ R.string.api_settings_save, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -50,57 +52,58 @@ public class AppSettingsActivity extends ActionBarActivity {
}
});
- setContentView(R.layout.api_app_settings_activity);
+ setContentView(R.layout.api_account_settings_activity);
- mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_app_settings_fragment);
+ mAccountSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_account_settings_fragment);
Intent intent = getIntent();
- mAppUri = intent.getData();
- if (mAppUri == null) {
+ mAccountUri = intent.getData();
+ if (mAccountUri == null) {
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
finish();
return;
} else {
- Log.d(Constants.TAG, "uri: " + mAppUri);
- loadData(mAppUri);
+ Log.d(Constants.TAG, "uri: " + mAccountUri);
+ loadData(savedInstanceState, mAccountUri);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.api_app_settings, menu);
+ getMenuInflater().inflate(R.menu.api_account_settings, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_api_settings_revoke:
- revokeAccess();
- return true;
- case R.id.menu_api_settings_cancel:
- finish();
- return true;
+ case R.id.menu_account_settings_delete:
+ deleteAccount();
+ return true;
+ case R.id.menu_account_settings_cancel:
+ finish();
+ return true;
}
return super.onOptionsItemSelected(item);
}
- private void loadData(Uri appUri) {
- AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
- mSettingsFragment.setAppSettings(settings);
+ private void loadData(Bundle savedInstanceState, Uri accountUri) {
+ // TODO: load this also like other fragment with newInstance arguments?
+ AccountSettings settings = ProviderHelper.getApiAccountSettings(this, accountUri);
+ mAccountSettingsFragment.setAccSettings(settings);
}
- private void revokeAccess() {
- if (getContentResolver().delete(mAppUri, null, null) <= 0) {
+ private void deleteAccount() {
+ if (getContentResolver().delete(mAccountUri, null, null) <= 0) {
throw new RuntimeException();
}
finish();
}
private void save() {
- ProviderHelper.updateApiApp(this, mSettingsFragment.getAppSettings(), mAppUri);
+ ProviderHelper.updateApiAccount(this, mAccountSettingsFragment.getAccSettings(), mAccountUri);
finish();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
new file mode 100644
index 000000000..0931e6e31
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -0,0 +1,176 @@
+/*
+ * 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
+ * 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.remote.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.ui.EditKeyActivity;
+import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
+import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
+import org.sufficientlysecure.keychain.util.AlgorithmNames;
+
+public class AccountSettingsFragment extends Fragment implements
+ SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
+
+ // model
+ private AccountSettings mAccSettings;
+
+ // view
+ private TextView mAccNameView;
+ private Spinner mEncryptionAlgorithm;
+ private Spinner mHashAlgorithm;
+ private Spinner mCompression;
+
+ private SelectSecretKeyLayoutFragment mSelectKeyFragment;
+ private BootstrapButton mCreateKeyButton;
+
+ KeyValueSpinnerAdapter mEncryptionAdapter;
+ KeyValueSpinnerAdapter mHashAdapter;
+ KeyValueSpinnerAdapter mCompressionAdapter;
+
+ public AccountSettings getAccSettings() {
+ return mAccSettings;
+ }
+
+ public void setAccSettings(AccountSettings accountSettings) {
+ this.mAccSettings = accountSettings;
+
+ mAccNameView.setText(accountSettings.getAccountName());
+ mSelectKeyFragment.selectKey(accountSettings.getKeyId());
+ mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(accountSettings
+ .getEncryptionAlgorithm()));
+ mHashAlgorithm.setSelection(mHashAdapter.getPosition(accountSettings.getHashAlgorithm()));
+ mCompression.setSelection(mCompressionAdapter.getPosition(accountSettings.getCompression()));
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.api_account_settings_fragment, container, false);
+ initView(view);
+ return view;
+ }
+
+ /**
+ * Set error String on key selection
+ *
+ * @param error
+ */
+ public void setErrorOnSelectKeyFragment(String error) {
+ mSelectKeyFragment.setError(error);
+ }
+
+ private void initView(View view) {
+ mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
+ R.id.api_account_settings_select_key_fragment);
+ mSelectKeyFragment.setCallback(this);
+
+ mAccNameView = (TextView) view.findViewById(R.id.api_account_settings_acc_name);
+ mEncryptionAlgorithm = (Spinner) view
+ .findViewById(R.id.api_account_settings_encryption_algorithm);
+ mHashAlgorithm = (Spinner) view.findViewById(R.id.api_account_settings_hash_algorithm);
+ mCompression = (Spinner) view.findViewById(R.id.api_account_settings_compression);
+ mCreateKeyButton = (BootstrapButton) view.findViewById(R.id.api_account_settings_create_key);
+
+ mCreateKeyButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createKey();
+ }
+ });
+
+ AlgorithmNames algorithmNames = new AlgorithmNames(getActivity());
+
+ mEncryptionAdapter = new KeyValueSpinnerAdapter(getActivity(),
+ algorithmNames.getEncryptionNames());
+ mEncryptionAlgorithm.setAdapter(mEncryptionAdapter);
+ mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mAccSettings.setEncryptionAlgorithm((int) id);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+ mHashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames());
+ mHashAlgorithm.setAdapter(mHashAdapter);
+ mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mAccSettings.setHashAlgorithm((int) id);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+ mCompressionAdapter = new KeyValueSpinnerAdapter(getActivity(),
+ algorithmNames.getCompressionNames());
+ mCompression.setAdapter(mCompressionAdapter);
+ mCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mAccSettings.setCompression((int) id);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+ }
+
+ private void createKey() {
+ Intent intent = new Intent(getActivity(), EditKeyActivity.class);
+ intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
+ intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
+ // set default user id to account name TODO: not working currently in EditKey
+ intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, mAccSettings.getAccountName());
+ startActivityForResult(intent, 0);
+ }
+
+ /**
+ * callback from select secret key fragment
+ */
+ @Override
+ public void onKeySelected(long secretKeyId) {
+ mAccSettings.setKeyId(secretKeyId);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java
new file mode 100644
index 000000000..8e65a2f04
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 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.remote.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+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.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.ui.widget.FixedListView;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class AccountsListFragment extends ListFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ private static final String ARG_DATA_URI = "uri";
+
+ // This is the Adapter being used to display the list's data.
+ AccountsAdapter mAdapter;
+
+ private Uri mDataUri;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static AccountsListFragment newInstance(Uri dataUri) {
+ AccountsListFragment frag = new AccountsListFragment();
+
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_DATA_URI, dataUri);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View layout = super.onCreateView(inflater, container,
+ savedInstanceState);
+ ListView lv = (ListView) layout.findViewById(android.R.id.list);
+ ViewGroup parent = (ViewGroup) lv.getParent();
+
+ /*
+ * http://stackoverflow.com/a/15880684
+ * Remove ListView and add FixedListView in its place.
+ * This is done here programatically to be still able to use the progressBar of ListFragment.
+ *
+ * We want FixedListView to be able to put this ListFragment inside a ScrollView
+ */
+ int lvIndex = parent.indexOfChild(lv);
+ parent.removeViewAt(lvIndex);
+ FixedListView newLv = new FixedListView(getActivity());
+ newLv.setId(android.R.id.list);
+ parent.addView(newLv, lvIndex, lv.getLayoutParams());
+ return layout;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mDataUri = getArguments().getParcelable(ARG_DATA_URI);
+
+ getListView().setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ String selectedAccountName = mAdapter.getItemAccountName(position);
+ Uri accountUri = mDataUri.buildUpon().appendEncodedPath(selectedAccountName).build();
+ Log.d(Constants.TAG, "accountUri: " + accountUri);
+
+ // edit account settings
+ Intent intent = new Intent(getActivity(), AccountSettingsActivity.class);
+ intent.setData(accountUri);
+ startActivity(intent);
+ }
+ });
+
+ // Give some text to display if there is no data. In a real
+ // application this would come from a resource.
+ setEmptyText(getString(R.string.api_settings_accounts_empty));
+
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
+
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new AccountsAdapter(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 Contacts rows that we will retrieve.
+ static final String[] PROJECTION = new String[]{
+ KeychainContract.ApiAccounts._ID, // 0
+ KeychainContract.ApiAccounts.ACCOUNT_NAME // 1
+ };
+
+ 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.
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(getActivity(), mDataUri, PROJECTION, null, null,
+ KeychainContract.ApiAccounts.ACCOUNT_NAME + " COLLATE LOCALIZED ASC");
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ private class AccountsAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ public AccountsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ /**
+ * Similar to CursorAdapter.getItemId().
+ * Required to build Uris for api app view, which is not based on row ids
+ *
+ * @param position
+ * @return
+ */
+ public String getItemAccountName(int position) {
+ if (mDataValid && mCursor != null) {
+ if (mCursor.moveToPosition(position)) {
+ return mCursor.getString(1);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView text = (TextView) view.findViewById(R.id.api_accounts_adapter_item_name);
+
+ String accountName = cursor.getString(1);
+ text.setText(accountName);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.api_accounts_adapter_list_item, null);
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
new file mode 100644
index 000000000..9e0ba49eb
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
@@ -0,0 +1,138 @@
+/*
+ * 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.remote.ui;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AppSettings;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class AppSettingsActivity extends ActionBarActivity {
+ private Uri mAppUri;
+
+ private AppSettingsFragment mSettingsFragment;
+ private AccountsListFragment mAccountsListFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // let the actionbar look like Android's contact app
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setIcon(android.R.color.transparent);
+ actionBar.setHomeButtonEnabled(true);
+
+ setContentView(R.layout.api_app_settings_activity);
+
+ mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
+
+ Intent intent = getIntent();
+ mAppUri = intent.getData();
+ if (mAppUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mAppUri);
+ loadData(savedInstanceState, mAppUri);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.api_app_settings, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_api_settings_revoke:
+ revokeAccess();
+ return true;
+ case R.id.menu_api_settings_cancel:
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void loadData(Bundle savedInstanceState, Uri appUri) {
+ // TODO: load this also like other fragment with newInstance arguments?
+ AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
+ mSettingsFragment.setAppSettings(settings);
+
+ String appName;
+ PackageManager pm = getPackageManager();
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(settings.getPackageName(), 0);
+ appName = (String) pm.getApplicationLabel(ai);
+ } catch (PackageManager.NameNotFoundException e) {
+ // fallback
+ appName = settings.getPackageName();
+ }
+ setTitle(appName);
+
+ Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build();
+ Log.d(Constants.TAG, "accountsUri: " + accountsUri);
+ startListFragment(savedInstanceState, accountsUri);
+ }
+
+ private void startListFragment(Bundle savedInstanceState, Uri dataUri) {
+ // However, if we're being restored from a previous state,
+ // then we don't need to do anything and should return or else
+ // we could end up with overlapping fragments.
+ if (savedInstanceState != null) {
+ return;
+ }
+
+ // Create an instance of the fragment
+ mAccountsListFragment = AccountsListFragment.newInstance(dataUri);
+
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.api_accounts_list_fragment, mAccountsListFragment)
+ .commitAllowingStateLoss();
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
+ }
+
+ private void revokeAccess() {
+ if (getContentResolver().delete(mAppUri, null, null) <= 0) {
+ throw new RuntimeException();
+ }
+ finish();
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java
new file mode 100644
index 000000000..a6db02708
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java
@@ -0,0 +1,108 @@
+/*
+ * 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
+ * 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.remote.ui;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.spongycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.remote.AppSettings;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class AppSettingsFragment extends Fragment {
+
+ // model
+ private AppSettings mAppSettings;
+
+ // view
+ private TextView mAppNameView;
+ private ImageView mAppIconView;
+ private TextView mPackageName;
+ private TextView mPackageSignature;
+
+ public AppSettings getAppSettings() {
+ return mAppSettings;
+ }
+
+ public void setAppSettings(AppSettings appSettings) {
+ this.mAppSettings = appSettings;
+ updateView(appSettings);
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
+ mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
+ mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
+ mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
+ mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
+ return view;
+ }
+
+ private void updateView(AppSettings appSettings) {
+ // get application name and icon from package manager
+ String appName;
+ Drawable appIcon = null;
+ PackageManager pm = getActivity().getApplicationContext().getPackageManager();
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(appSettings.getPackageName(), 0);
+
+ appName = (String) pm.getApplicationLabel(ai);
+ appIcon = pm.getApplicationIcon(ai);
+ } catch (NameNotFoundException e) {
+ // fallback
+ appName = appSettings.getPackageName();
+ }
+ mAppNameView.setText(appName);
+ mAppIconView.setImageDrawable(appIcon);
+
+ // advanced info: package name
+ mPackageName.setText(appSettings.getPackageName());
+
+ // advanced info: package signature SHA-256
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(appSettings.getPackageSignature());
+ byte[] digest = md.digest();
+ String signature = new String(Hex.encode(digest));
+
+ mPackageSignature.setText(signature);
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(Constants.TAG, "Should not happen!", e);
+ }
+ }
+
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java
index 3c553fff5..f86d279f0 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java
@@ -15,14 +15,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
+import android.os.Bundle;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.DrawerActivity;
-import android.os.Bundle;
-
-public class RegisteredAppsListActivity extends DrawerActivity {
+public class AppsListActivity extends DrawerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java
index fed267a44..f3fa6e7c6 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java
@@ -15,14 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
-
-import android.content.ContentUris;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -30,11 +28,22 @@ 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.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
+import org.sufficientlysecure.keychain.util.Log;
-public class RegisteredAppsListFragment extends ListFragment implements
+public class AppsListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
// This is the Adapter being used to display the list's data.
@@ -47,9 +56,10 @@ public class RegisteredAppsListFragment extends ListFragment implements
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ String selectedPackageName = mAdapter.getItemPackageName(position);
// edit app settings
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
- intent.setData(ContentUris.withAppendedId(KeychainContract.ApiApps.CONTENT_URI, id));
+ intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName));
startActivity(intent);
}
});
@@ -71,7 +81,10 @@ public class RegisteredAppsListFragment extends ListFragment implements
}
// These are the Contacts rows that we will retrieve.
- static final String[] PROJECTION = new String[] { ApiApps._ID, ApiApps.PACKAGE_NAME };
+ static final String[] PROJECTION = new String[]{
+ ApiApps._ID, // 0
+ ApiApps.PACKAGE_NAME // 1
+ };
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
@@ -99,4 +112,65 @@ public class RegisteredAppsListFragment extends ListFragment implements
mAdapter.swapCursor(null);
}
-} \ No newline at end of file
+ private class RegisteredAppsAdapter extends CursorAdapter {
+
+ private LayoutInflater mInflater;
+ private PackageManager mPM;
+
+ public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ mPM = context.getApplicationContext().getPackageManager();
+ }
+
+ /**
+ * Similar to CursorAdapter.getItemId().
+ * Required to build Uris for api app view, which is not based on row ids
+ *
+ * @param position
+ * @return
+ */
+ public String getItemPackageName(int position) {
+ if (mDataValid && mCursor != null) {
+ if (mCursor.moveToPosition(position)) {
+ return mCursor.getString(1);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
+ ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
+
+ String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
+ if (packageName != null) {
+ // get application name
+ try {
+ ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0);
+
+ text.setText(mPM.getApplicationLabel(ai));
+ icon.setImageDrawable(mPM.getApplicationIcon(ai));
+ } catch (final PackageManager.NameNotFoundException e) {
+ // fallback
+ text.setText(packageName);
+ }
+ } else {
+ // fallback
+ text.setText(packageName);
+ }
+
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
index 11b3ee217..a894da448 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
import android.content.Intent;
import android.os.Bundle;
@@ -32,7 +32,10 @@ import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -42,6 +45,8 @@ import java.util.ArrayList;
public class RemoteServiceActivity extends ActionBarActivity {
public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER";
+ public static final String ACTION_CREATE_ACCOUNT = Constants.INTENT_PREFIX
+ + "API_ACTIVITY_CREATE_ACCOUNT";
public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX
+ "API_ACTIVITY_CACHE_PASSPHRASE";
public static final String ACTION_SELECT_PUB_KEYS = Constants.INTENT_PREFIX
@@ -58,6 +63,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
// register action
public static final String EXTRA_PACKAGE_NAME = "package_name";
public static final String EXTRA_PACKAGE_SIGNATURE = "package_signature";
+ // create acc action
+ public static final String EXTRA_ACC_NAME = "acc_name";
// select pub keys action
public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids";
@@ -66,7 +73,9 @@ public class RemoteServiceActivity extends ActionBarActivity {
public static final String EXTRA_ERROR_MESSAGE = "error_message";
// register view
- private AppSettingsFragment mSettingsFragment;
+ private AppSettingsFragment mAppSettingsFragment;
+ // create acc view
+ private AccountSettingsFragment mAccSettingsFragment;
// select pub keys view
private SelectPublicKeyFragment mSelectFragment;
@@ -86,21 +95,62 @@ public class RemoteServiceActivity extends ActionBarActivity {
if (ACTION_REGISTER.equals(action)) {
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
+ Log.d(Constants.TAG, "ACTION_REGISTER packageName: "+packageName);
// Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.api_register_allow,
+ ActionBarHelper.setTwoButtonView(getSupportActionBar(),
+ R.string.api_register_allow, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {
// Allow
+ ProviderHelper.insertApiApp(RemoteServiceActivity.this,
+ mAppSettingsFragment.getAppSettings());
+
+ // give data through for new service call
+ Intent resultData = extras.getParcelable(EXTRA_DATA);
+ RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
+ RemoteServiceActivity.this.finish();
+ }
+ }, R.string.api_register_disallow, R.drawable.ic_action_cancel,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Disallow
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
+ }
+ }
+ );
+
+ setContentView(R.layout.api_remote_register_app);
+
+ mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
+
+ AppSettings settings = new AppSettings(packageName, packageSignature);
+ mAppSettingsFragment.setAppSettings(settings);
+ } else if (ACTION_CREATE_ACCOUNT.equals(action)) {
+ final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
+ final String accName = extras.getString(EXTRA_ACC_NAME);
+
+ // Inflate a "Done"/"Cancel" custom action bar view
+ ActionBarHelper.setTwoButtonView(getSupportActionBar(),
+ R.string.api_settings_save, R.drawable.ic_action_done,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Save
+
// user needs to select a key!
- if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
- mSettingsFragment.setErrorOnSelectKeyFragment(
+ if (mAccSettingsFragment.getAccSettings().getKeyId() == Id.key.none) {
+ mAccSettingsFragment.setErrorOnSelectKeyFragment(
getString(R.string.api_register_error_select_key));
} else {
- ProviderHelper.insertApiApp(RemoteServiceActivity.this,
- mSettingsFragment.getAppSettings());
+ ProviderHelper.insertApiAccount(RemoteServiceActivity.this,
+ KeychainContract.ApiAccounts.buildBaseUri(packageName),
+ mAccSettingsFragment.getAccSettings());
// give data through for new service call
Intent resultData = extras.getParcelable(EXTRA_DATA);
@@ -108,23 +158,24 @@ public class RemoteServiceActivity extends ActionBarActivity {
RemoteServiceActivity.this.finish();
}
}
- }, R.string.api_register_disallow, new View.OnClickListener() {
+ }, R.string.api_settings_cancel, R.drawable.ic_action_cancel,
+ new View.OnClickListener() {
@Override
public void onClick(View v) {
- // Disallow
+ // Cancel
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
RemoteServiceActivity.this.finish();
}
}
);
- setContentView(R.layout.api_app_register_activity);
+ setContentView(R.layout.api_remote_create_account);
- mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_app_settings_fragment);
+ mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_account_settings_fragment);
- AppSettings settings = new AppSettings(packageName, packageSignature);
- mSettingsFragment.setAppSettings(settings);
+ AccountSettings settings = new AccountSettings(accName);
+ mAccSettingsFragment.setAccSettings(settings);
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
Intent resultData = extras.getParcelable(EXTRA_DATA);
@@ -161,7 +212,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
// Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
+ ActionBarHelper.setTwoButtonView(getSupportActionBar(),
+ R.string.btn_okay, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -173,7 +225,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
RemoteServiceActivity.this.finish();
}
- }, R.string.btn_do_not_save, new View.OnClickListener() {
+ }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
@Override
public void onClick(View v) {
// cancel
@@ -183,7 +235,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
);
- setContentView(R.layout.api_app_select_pub_keys_activity);
+ setContentView(R.layout.api_remote_select_pub_keys);
// set text on view
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text);
@@ -214,7 +266,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
String text = "<font color=\"red\">" + errorMessage + "</font>";
// Inflate a "Done" custom action bar view
- ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_okay,
+ ActionBarHelper.setOneButtonView(getSupportActionBar(),
+ R.string.btn_okay, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
@@ -224,7 +277,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
});
- setContentView(R.layout.api_app_error_message);
+ setContentView(R.layout.api_remote_error_message);
// set text on view
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index d796ccf31..846eb8cf9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -17,62 +17,41 @@
package org.sufficientlysecure.keychain.service;
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.GregorianCalendar;
-import java.util.List;
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.*;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
-import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.pgp.PgpImportExport;
-import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
-import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
+import org.sufficientlysecure.keychain.pgp.*;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
-import org.sufficientlysecure.keychain.util.HkpKeyServer;
-import org.sufficientlysecure.keychain.util.InputData;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
+import org.sufficientlysecure.keychain.util.*;
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.List;
/**
* This Service contains all important long lasting operations for APG. It receives Intents with
* data from the activities or other apps, queues these intents, executes them, and stops itself
* after doing them.
*/
-public class KeychainIntentService extends IntentService implements ProgressDialogUpdater {
+public class KeychainIntentService extends IntentService
+ implements ProgressDialogUpdater, KeychainServiceListener {
/* extras that can be given by intent */
public static final String EXTRA_MESSENGER = "messenger";
@@ -159,6 +138,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
// sign key
public static final String CERTIFY_KEY_MASTER_KEY_ID = "sign_key_master_key_id";
public static final String CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
+ public static final String CERTIFY_KEY_UIDS = "sign_key_uids";
/*
* possible data keys as result send over messenger
@@ -324,8 +304,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
builder.enableAsciiArmorOutput(useAsciiArmor)
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
.signatureKeyId(secretKeyId)
- .signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
+ .signatureHashAlgorithm(
+ Preferences.getPreferences(this).getDefaultHashAlgorithm())
+ .signaturePassphrase(
+ PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
builder.build().generateSignature();
} else if (signOnly) {
@@ -333,21 +315,26 @@ public class KeychainIntentService extends IntentService implements ProgressDial
builder.enableAsciiArmorOutput(useAsciiArmor)
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
.signatureKeyId(secretKeyId)
- .signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
+ .signatureHashAlgorithm(
+ Preferences.getPreferences(this).getDefaultHashAlgorithm())
+ .signaturePassphrase(
+ PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
builder.build().execute();
} else {
Log.d(Constants.TAG, "encrypt...");
builder.enableAsciiArmorOutput(useAsciiArmor)
.compressionId(compressionId)
- .symmetricEncryptionAlgorithm(Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
+ .symmetricEncryptionAlgorithm(
+ Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
.encryptionKeyIds(encryptionKeyIds)
.encryptionPassphrase(encryptionPassphrase)
.signatureKeyId(secretKeyId)
- .signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
+ .signatureHashAlgorithm(
+ Preferences.getPreferences(this).getDefaultHashAlgorithm())
+ .signaturePassphrase(
+ PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
builder.build().execute();
}
@@ -586,13 +573,24 @@ public class KeychainIntentService extends IntentService implements ProgressDial
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
/* Operation */
+ int keysTotal = 2;
+ int keysCreated = 0;
+ setProgress(
+ getApplicationContext().getResources().
+ getQuantityString(R.plurals.progress_generating, keysTotal),
+ keysCreated,
+ keysTotal);
PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
4096, passphrase, true);
+ keysCreated++;
+ setProgress(keysCreated, keysTotal);
PGPSecretKey subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
4096, passphrase, false);
+ keysCreated++;
+ setProgress(keysCreated, keysTotal);
// TODO: default to one master for cert, one sub for encrypt and one sub
// for sign
@@ -652,14 +650,11 @@ public class KeychainIntentService extends IntentService implements ProgressDial
if (data.containsKey(EXPORT_KEY_TYPE)) {
keyType = data.getInt(EXPORT_KEY_TYPE);
}
-
+ long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
String outputFile = data.getString(EXPORT_FILENAME);
+ // If not exporting all keys get the masterKeyIds of the keys to export from the intent
boolean exportAll = data.getBoolean(EXPORT_ALL);
- long keyRingMasterKeyId = -1;
- if (!exportAll) {
- keyRingMasterKeyId = data.getLong(EXPORT_KEY_RING_MASTER_KEY_ID);
- }
/* Operation */
@@ -668,27 +663,42 @@ public class KeychainIntentService extends IntentService implements ProgressDial
throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
}
- // OutputStream
- FileOutputStream outStream = new FileOutputStream(outputFile);
+ ArrayList<Long> publicMasterKeyIds = new ArrayList<Long>();
+ ArrayList<Long> secretMasterKeyIds = new ArrayList<Long>();
+ ArrayList<Long> allPublicMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this);
+ ArrayList<Long> allSecretMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this);
- ArrayList<Long> keyRingMasterKeyIds = new ArrayList<Long>();
if (exportAll) {
- // get all key ring row ids based on export type
-
- if (keyType == Id.type.public_key) {
- keyRingMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this);
- } else {
- keyRingMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this);
+ // get all public key ring MasterKey ids
+ if (keyType == Id.type.public_key || keyType == Id.type.public_secret_key) {
+ publicMasterKeyIds = allPublicMasterKeyIds;
+ }
+ // get all secret key ring MasterKey ids
+ if (keyType == Id.type.secret_key || keyType == Id.type.public_secret_key) {
+ secretMasterKeyIds = allSecretMasterKeyIds;
}
} else {
- keyRingMasterKeyIds.add(keyRingMasterKeyId);
+
+ for (long masterKeyId : masterKeyIds) {
+ if ((keyType == Id.type.public_key || keyType == Id.type.public_secret_key)
+ && allPublicMasterKeyIds.contains(masterKeyId)) {
+ publicMasterKeyIds.add(masterKeyId);
+ }
+ if ((keyType == Id.type.secret_key || keyType == Id.type.public_secret_key)
+ && allSecretMasterKeyIds.contains(masterKeyId)) {
+ secretMasterKeyIds.add(masterKeyId);
+ }
+ }
}
- Bundle resultData = new Bundle();
+ PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);
+ Bundle resultData = pgpImportExport
+ .exportKeyRings(publicMasterKeyIds, secretMasterKeyIds,
+ new FileOutputStream(outputFile));
- PgpImportExport pgpImportExport = new PgpImportExport(this, this);
- resultData = pgpImportExport
- .exportKeyRings(keyRingMasterKeyIds, keyType, outStream);
+ if (mIsCanceled) {
+ boolean isDeleted = new File(outputFile).delete();
+ }
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {
@@ -724,48 +734,58 @@ public class KeychainIntentService extends IntentService implements ProgressDial
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
+ // TODO: add extra which requires fingerprint suport and force verification!
+ // only supported by newer sks keyserver versions
+
// this downloads the keys and places them into the ImportKeysListEntry entries
HkpKeyServer server = new HkpKeyServer(keyServer);
for (ImportKeysListEntry entry : entries) {
- byte[] downloadedKey = server.get(entry.getKeyId()).getBytes();
-
- /**
- * TODO: copied from ImportKeysListLoader
- *
- *
- * this parses the downloaded key
- */
- // need to have access to the bufferedInput, so we can reuse it for the possible
- // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
- // armor blocks
- BufferedInputStream bufferedInput = new BufferedInputStream(new ByteArrayInputStream(downloadedKey));
- try {
-
- // read all available blocks... (asc files can contain many blocks with BEGIN END)
- while (bufferedInput.available() > 0) {
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // go through all objects in this block
- Object obj;
- while ((obj = objectFactory.nextObject()) != null) {
- Log.d(Constants.TAG, "Found class: " + obj.getClass());
-
- if (obj instanceof PGPKeyRing) {
- PGPKeyRing newKeyring = (PGPKeyRing) obj;
-
- entry.setBytes(newKeyring.getEncoded());
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
- }
+ // if available use complete fingerprint for get request
+ byte[] downloadedKeyBytes;
+ if (entry.getFingerPrintHex() != null) {
+ downloadedKeyBytes = server.get("0x" + entry.getFingerPrintHex()).getBytes();
+ } else {
+ downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
+ }
+
+ // create PGPKeyRing object based on downloaded armored key
+ PGPKeyRing downloadedKey = null;
+ BufferedInputStream bufferedInput =
+ new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
+ if (bufferedInput.available() > 0) {
+ InputStream in = PGPUtil.getDecoderStream(bufferedInput);
+ PGPObjectFactory objectFactory = new PGPObjectFactory(in);
+
+ // get first object in block
+ Object obj;
+ if ((obj = objectFactory.nextObject()) != null) {
+ Log.d(Constants.TAG, "Found class: " + obj.getClass());
+
+ if (obj instanceof PGPKeyRing) {
+ downloadedKey = (PGPKeyRing) obj;
+ } else {
+ throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
- } catch (Exception e) {
- Log.e(Constants.TAG, "Exception on parsing key file!", e);
}
+
+ // verify downloaded key by comparing fingerprints
+ if (entry.getFingerPrintHex() != null) {
+ String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(downloadedKey.getPublicKey().getFingerprint());
+ if (downloadedKeyFp.equals(entry.getFingerPrintHex())) {
+ Log.d(Constants.TAG, "fingerprint of downloaded key is the same as the requested fingerprint!");
+ } else {
+ throw new PgpGeneralException("fingerprint of downloaded key is NOT the same as the requested fingerprint!");
+ }
+ }
+
+ // save key bytes in entry object for doing the
+ // actual import afterwards
+ entry.setBytes(downloadedKey.getEncoded());
}
+
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
@@ -786,6 +806,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
/* Input */
long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID);
long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID);
+ ArrayList<String> userIds = data.getStringArrayList(CERTIFY_KEY_UIDS);
/* Operation */
String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
@@ -793,7 +814,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
- signaturePassPhrase);
+ userIds, signaturePassPhrase);
// store the signed key in our local cache
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
@@ -811,10 +832,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
private void sendErrorToHandler(Exception e) {
// Service was canceled. Do not send error to handler.
- if (this.mIsCanceled)
+ if (this.mIsCanceled) {
return;
-
- Log.e(Constants.TAG, "ApgService Exception: ", e);
+ }
+ Log.e(Constants.TAG, "KeychainIntentService Exception: ", e);
e.printStackTrace();
Bundle data = new Bundle();
@@ -824,9 +845,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
private void sendMessageToHandler(Integer arg1, Integer arg2, Bundle data) {
// Service was canceled. Do not send message to handler.
- if (this.mIsCanceled)
+ if (this.mIsCanceled) {
return;
-
+ }
Message msg = Message.obtain();
msg.arg1 = arg1;
if (arg2 != null) {
@@ -877,4 +898,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public void setProgress(int progress, int max) {
setProgress(null, progress, max);
}
+
+ @Override
+ public boolean hasServiceStopped() {
+ return mIsCanceled;
+ }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
index 6eba9cc83..92d012c80 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
@@ -17,11 +17,7 @@
package org.sufficientlysecure.keychain.service;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
-import org.sufficientlysecure.keychain.R;
-
import android.app.Activity;
-import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.os.Bundle;
import android.os.Handler;
@@ -29,6 +25,8 @@ import android.os.Message;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
public class KeychainIntentServiceHandler extends Handler {
@@ -51,25 +49,31 @@ public class KeychainIntentServiceHandler extends Handler {
this.mActivity = activity;
}
- public KeychainIntentServiceHandler(Activity activity, ProgressDialogFragment progressDialogFragment) {
+ public KeychainIntentServiceHandler(Activity activity,
+ ProgressDialogFragment progressDialogFragment) {
this.mActivity = activity;
this.mProgressDialogFragment = progressDialogFragment;
}
- public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) {
- this(activity, progressDialogMessageId, progressDialogStyle, false, null);
+ public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage,
+ int progressDialogStyle) {
+ this(activity, progressDialogMessage, progressDialogStyle, false, null);
}
- public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId,
+ public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage,
int progressDialogStyle, boolean cancelable,
OnCancelListener onCancelListener) {
this.mActivity = activity;
- this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId,
- progressDialogStyle, cancelable, onCancelListener);
+ this.mProgressDialogFragment = ProgressDialogFragment.newInstance(
+ progressDialogMessage,
+ progressDialogStyle,
+ cancelable,
+ onCancelListener);
}
public void showProgressDialog(FragmentActivity activity) {
- // TODO: This is a hack!, see http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
+ // TODO: This is a hack!, see
+ // http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
final FragmentManager manager = activity.getSupportFragmentManager();
Handler handler = new Handler();
handler.post(new Runnable() {
@@ -84,43 +88,43 @@ public class KeychainIntentServiceHandler extends Handler {
Bundle data = message.getData();
switch (message.arg1) {
- case MESSAGE_OKAY:
- mProgressDialogFragment.dismissAllowingStateLoss();
+ case MESSAGE_OKAY:
+ mProgressDialogFragment.dismissAllowingStateLoss();
- break;
+ break;
- case MESSAGE_EXCEPTION:
- mProgressDialogFragment.dismissAllowingStateLoss();
+ case MESSAGE_EXCEPTION:
+ mProgressDialogFragment.dismissAllowingStateLoss();
- // show error from service
- if (data.containsKey(DATA_ERROR)) {
- Toast.makeText(mActivity,
- mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
- Toast.LENGTH_SHORT).show();
- }
+ // show error from service
+ if (data.containsKey(DATA_ERROR)) {
+ Toast.makeText(mActivity,
+ mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
+ Toast.LENGTH_SHORT).show();
+ }
- break;
-
- case MESSAGE_UPDATE_PROGRESS:
- if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) {
-
- // update progress from service
- if (data.containsKey(DATA_MESSAGE)) {
- mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE),
- data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
- } else if (data.containsKey(DATA_MESSAGE_ID)) {
- mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID),
- data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
- } else {
- mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS),
- data.getInt(DATA_PROGRESS_MAX));
+ break;
+
+ case MESSAGE_UPDATE_PROGRESS:
+ if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) {
+
+ // update progress from service
+ if (data.containsKey(DATA_MESSAGE)) {
+ mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE),
+ data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
+ } else if (data.containsKey(DATA_MESSAGE_ID)) {
+ mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID),
+ data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
+ } else {
+ mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS),
+ data.getInt(DATA_PROGRESS_MAX));
+ }
}
- }
- break;
+ break;
- default:
- break;
+ default:
+ break;
}
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index abd2320e3..5825db01b 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -17,10 +17,16 @@
package org.sufficientlysecure.keychain.service;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.*;
+import android.util.Log;
+import android.support.v4.util.LongSparseArray;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
@@ -33,28 +39,13 @@ import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
+import java.util.Date;
+import java.util.Iterator;
/**
* This service runs in its own process, but is available to all other processes as the main
* passphrase cache. Use the static methods addCachedPassphrase and getCachedPassphrase for
* convenience.
- *
*/
public class PassphraseCacheService extends Service {
public static final String TAG = Constants.TAG + ": PassphraseCacheService";
@@ -77,7 +68,7 @@ public class PassphraseCacheService extends Service {
private BroadcastReceiver mIntentReceiver;
- private HashMap<Long, String> mPassphraseCache = new HashMap<Long, String>();
+ private LongSparseArray<String> mPassphraseCache = new LongSparseArray<String>();
Context mContext;
@@ -85,7 +76,7 @@ public class PassphraseCacheService extends Service {
* This caches a new passphrase in memory by sending a new command to the service. An android
* service is only run once. Thus, when the service is already started, new commands just add
* new events to the alarm manager for new passphrases to let them timeout in the future.
- *
+ *
* @param context
* @param keyId
* @param passphrase
@@ -105,7 +96,7 @@ public class PassphraseCacheService extends Service {
/**
* Gets a cached passphrase from memory by sending an intent to the service. This method is
* designed to wait until the service returns the passphrase.
- *
+ *
* @param context
* @param keyId
* @return passphrase or null (if no passphrase is cached for this keyId)
@@ -160,7 +151,7 @@ public class PassphraseCacheService extends Service {
/**
* Internal implementation to get cached passphrase.
- *
+ *
* @param keyId
* @return
*/
@@ -204,7 +195,7 @@ public class PassphraseCacheService extends Service {
/**
* Checks if key has a passphrase.
- *
+ *
* @param secretKeyId
* @return true if it has a passphrase
*/
@@ -215,17 +206,17 @@ public class PassphraseCacheService extends Service {
.getPGPSecretKeyRingByKeyId(context, secretKeyId);
PGPSecretKey secretKey = null;
boolean foundValidKey = false;
- for (Iterator keys = secRing.getSecretKeys(); keys.hasNext();) {
- secretKey = (PGPSecretKey)keys.next();
+ for (Iterator keys = secRing.getSecretKeys(); keys.hasNext(); ) {
+ secretKey = (PGPSecretKey) keys.next();
if (!secretKey.isPrivateKeyEmpty()) {
foundValidKey = true;
break;
}
}
- if (!foundValidKey)
+ if (!foundValidKey) {
return false;
-
+ }
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
"SC").build("".toCharArray());
PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
@@ -268,7 +259,7 @@ public class PassphraseCacheService extends Service {
/**
* Build pending intent that is executed by alarm manager to time out a specific passphrase
- *
+ *
* @param context
* @param keyId
* @return
@@ -336,7 +327,7 @@ public class PassphraseCacheService extends Service {
/**
* Called when one specific passphrase for keyId timed out
- *
+ *
* @param context
* @param keyId
*/
@@ -347,7 +338,7 @@ public class PassphraseCacheService extends Service {
Log.d(TAG, "Timeout of keyId " + keyId + ", removed from memory!");
// stop whole service if no cached passphrases remaining
- if (mPassphraseCache.isEmpty()) {
+ if (mPassphraseCache.size() == 0) {
Log.d(TAG, "No passphrases remaining in memory, stopping service!");
stopSelf();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
deleted file mode 100644
index 64c4c5e96..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * 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
- * 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.service.remote;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-import org.spongycastle.util.encoders.Hex;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
-import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
-import org.sufficientlysecure.keychain.util.AlgorithmNames;
-import org.sufficientlysecure.keychain.util.Log;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import com.beardedhen.androidbootstrap.BootstrapButton;
-
-public class AppSettingsFragment extends Fragment implements
- SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
-
- // model
- private AppSettings appSettings;
-
- // view
- private LinearLayout mAdvancedSettingsContainer;
- private BootstrapButton mAdvancedSettingsButton;
- private TextView mAppNameView;
- private ImageView mAppIconView;
- private Spinner mEncryptionAlgorithm;
- private Spinner mHashAlgorithm;
- private Spinner mCompression;
- private TextView mPackageName;
- private TextView mPackageSignature;
-
- private SelectSecretKeyLayoutFragment mSelectKeyFragment;
-
- KeyValueSpinnerAdapter encryptionAdapter;
- KeyValueSpinnerAdapter hashAdapter;
- KeyValueSpinnerAdapter compressionAdapter;
-
- public AppSettings getAppSettings() {
- return appSettings;
- }
-
- public void setAppSettings(AppSettings appSettings) {
- this.appSettings = appSettings;
- setPackage(appSettings.getPackageName());
- mPackageName.setText(appSettings.getPackageName());
-
- try {
- MessageDigest md = MessageDigest.getInstance("SHA-256");
- md.update(appSettings.getPackageSignature());
- byte[] digest = md.digest();
- String signature = new String(Hex.encode(digest));
-
- mPackageSignature.setText(signature);
- } catch (NoSuchAlgorithmException e) {
- Log.e(Constants.TAG, "Should not happen!", e);
- }
-
- mSelectKeyFragment.selectKey(appSettings.getKeyId());
- mEncryptionAlgorithm.setSelection(encryptionAdapter.getPosition(appSettings
- .getEncryptionAlgorithm()));
- mHashAlgorithm.setSelection(hashAdapter.getPosition(appSettings.getHashAlgorithm()));
- mCompression.setSelection(compressionAdapter.getPosition(appSettings.getCompression()));
- }
-
- /**
- * Inflate the layout for this fragment
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
- initView(view);
- return view;
- }
-
- /**
- * Set error String on key selection
- *
- * @param error
- */
- public void setErrorOnSelectKeyFragment(String error) {
- mSelectKeyFragment.setError(error);
- }
-
- private void initView(View view) {
- mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
- R.id.api_app_settings_select_key_fragment);
- mSelectKeyFragment.setCallback(this);
-
- mAdvancedSettingsButton = (BootstrapButton) view
- .findViewById(R.id.api_app_settings_advanced_button);
- mAdvancedSettingsContainer = (LinearLayout) view
- .findViewById(R.id.api_app_settings_advanced);
-
- mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
- mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
- mEncryptionAlgorithm = (Spinner) view
- .findViewById(R.id.api_app_settings_encryption_algorithm);
- mHashAlgorithm = (Spinner) view.findViewById(R.id.api_app_settings_hash_algorithm);
- mCompression = (Spinner) view.findViewById(R.id.api_app_settings_compression);
- mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
- mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
-
- AlgorithmNames algorithmNames = new AlgorithmNames(getActivity());
-
- encryptionAdapter = new KeyValueSpinnerAdapter(getActivity(),
- algorithmNames.getEncryptionNames());
- mEncryptionAlgorithm.setAdapter(encryptionAdapter);
- mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- appSettings.setEncryptionAlgorithm((int) id);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
- hashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames());
- mHashAlgorithm.setAdapter(hashAdapter);
- mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- appSettings.setHashAlgorithm((int) id);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
- compressionAdapter = new KeyValueSpinnerAdapter(getActivity(),
- algorithmNames.getCompressionNames());
- mCompression.setAdapter(compressionAdapter);
- mCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- appSettings.setCompression((int) id);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
- final Animation visibleAnimation = new AlphaAnimation(0.0f, 1.0f);
- visibleAnimation.setDuration(250);
- final Animation invisibleAnimation = new AlphaAnimation(1.0f, 0.0f);
- invisibleAnimation.setDuration(250);
-
- // TODO: Better: collapse/expand animation
- // final Animation animation2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
- // Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f,
- // Animation.RELATIVE_TO_SELF, 0.0f);u
- // animation2.setDuration(150);
-
- mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) {
- mAdvancedSettingsContainer.startAnimation(invisibleAnimation);
- mAdvancedSettingsContainer.setVisibility(View.GONE);
- mAdvancedSettingsButton.setText(getString(R.string.api_settings_show_advanced));
- mAdvancedSettingsButton.setLeftIcon("fa-caret-up");
- } else {
- mAdvancedSettingsContainer.startAnimation(visibleAnimation);
- mAdvancedSettingsContainer.setVisibility(View.VISIBLE);
- mAdvancedSettingsButton.setText(getString(R.string.api_settings_hide_advanced));
- mAdvancedSettingsButton.setLeftIcon("fa-caret-down");
- }
- }
- });
- }
-
- private void setPackage(String packageName) {
- PackageManager pm = getActivity().getApplicationContext().getPackageManager();
-
- // get application name and icon from package manager
- String appName = null;
- Drawable appIcon = null;
- try {
- ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
-
- appName = (String) pm.getApplicationLabel(ai);
- appIcon = pm.getApplicationIcon(ai);
- } catch (final NameNotFoundException e) {
- // fallback
- appName = packageName;
- }
- mAppNameView.setText(appName);
- mAppIconView.setImageDrawable(appIcon);
- }
-
- /**
- * callback from select secret key fragment
- */
- @Override
- public void onKeySelected(long secretKeyId) {
- appSettings.setKeyId(secretKeyId);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java
deleted file mode 100644
index 477ee04d0..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java
+++ /dev/null
@@ -1,76 +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.service.remote;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.Cursor;
-import android.support.v4.widget.CursorAdapter;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-public class RegisteredAppsAdapter extends CursorAdapter {
-
- private LayoutInflater mInflater;
- private PackageManager pm;
-
- public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
- super(context, c, flags);
-
- mInflater = LayoutInflater.from(context);
- pm = context.getApplicationContext().getPackageManager();
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
- ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
-
- String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
- if (packageName != null) {
- // get application name
- try {
- ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
-
- text.setText(pm.getApplicationLabel(ai));
- icon.setImageDrawable(pm.getApplicationIcon(ai));
- } catch (final NameNotFoundException e) {
- // fallback
- text.setText(packageName);
- }
- } else {
- // fallback
- text.setText(packageName);
- }
-
- }
-
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java
deleted file mode 100644
index cc08548e8..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.sufficientlysecure.keychain.service.remote;
-
-public class WrongPackageSignatureException extends Exception {
-
- private static final long serialVersionUID = -8294642703122196028L;
-
- public WrongPackageSignatureException(String message) {
- super(message);
- }
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index aab6b81d9..5dc06c16d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -17,47 +17,47 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.Iterator;
-
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.app.ProgressDialog;
import android.content.Intent;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
+import android.widget.*;
import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.Spinner;
-import android.widget.Toast;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
/**
* Signs the specified public key with the specified secret master key
*/
public class CertifyKeyActivity extends ActionBarActivity implements
- SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
+ SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks<Cursor> {
private BootstrapButton mSignButton;
private CheckBox mUploadKeyCheckbox;
private Spinner mSelectKeyserverSpinner;
@@ -68,6 +68,12 @@ public class CertifyKeyActivity extends ActionBarActivity implements
private long mPubKeyId = 0;
private long mMasterKeyId = 0;
+ private ListView mUserIds;
+ private ViewKeyUserIdsAdapter mUserIdsAdapter;
+
+ private static final int LOADER_ID_KEYRING = 0;
+ private static final int LOADER_ID_USER_IDS = 1;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -87,7 +93,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
mSelectKeyserverSpinner = (Spinner) findViewById(R.id.sign_key_keyserver);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
- .getKeyServers());
+ .getKeyServers());
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSelectKeyserverSpinner.setAdapter(adapter);
@@ -131,9 +137,18 @@ public class CertifyKeyActivity extends ActionBarActivity implements
finish();
return;
}
+ Log.e(Constants.TAG, "uri: " + mDataUri);
PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, mDataUri);
+ mUserIds = (ListView) findViewById(R.id.user_ids);
+
+ mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0, true);
+ mUserIds.setAdapter(mUserIdsAdapter);
+
+ getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
+ getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
+
if (signKey != null) {
mPubKeyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
}
@@ -144,6 +159,78 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
}
+ static final String[] KEYRING_PROJECTION =
+ new String[] {
+ KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.Keys.FINGERPRINT,
+ KeychainContract.UserIds.USER_ID
+ };
+ static final int INDEX_MASTER_KEY_ID = 1;
+ static final int INDEX_FINGERPRINT = 2;
+ static final int INDEX_USER_ID = 3;
+
+ static final String[] USER_IDS_PROJECTION =
+ new String[]{
+ KeychainContract.UserIds._ID,
+ KeychainContract.UserIds.USER_ID,
+ KeychainContract.UserIds.RANK
+ };
+ static final String USER_IDS_SORT_ORDER =
+ KeychainContract.UserIds.RANK + " ASC";
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ switch(id) {
+ case LOADER_ID_KEYRING:
+ return new CursorLoader(this, mDataUri, KEYRING_PROJECTION, null, null, null);
+ case LOADER_ID_USER_IDS: {
+ Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri);
+ return new CursorLoader(this, baseUri, USER_IDS_PROJECTION, null, null, USER_IDS_SORT_ORDER);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ switch(loader.getId()) {
+ case LOADER_ID_KEYRING:
+ // the first key here is our master key
+ if (data.moveToFirst()) {
+ // TODO: put findViewById in onCreate!
+
+ long keyId = data.getLong(INDEX_MASTER_KEY_ID);
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(keyId);
+ ((TextView) findViewById(R.id.key_id)).setText(keyIdStr);
+
+ String mainUserId = data.getString(INDEX_USER_ID);
+ ((TextView) findViewById(R.id.main_user_id)).setText(mainUserId);
+
+ byte[] fingerprintBlob = data.getBlob(INDEX_FINGERPRINT);
+ if (fingerprintBlob == null) {
+ // FALLBACK for old database entries
+ fingerprintBlob = ProviderHelper.getFingerprint(this, mDataUri);
+ }
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
+ ((TextView) findViewById(R.id.fingerprint)).setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
+ }
+ break;
+ case LOADER_ID_USER_IDS:
+ mUserIdsAdapter.swapCursor(data);
+ break;
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ switch(loader.getId()) {
+ case LOADER_ID_USER_IDS:
+ mUserIdsAdapter.swapCursor(null);
+ break;
+ }
+ }
+
private void showPassphraseDialog(final long secretKeyId) {
// Message is received after passphrase is cached
Handler returnHandler = new Handler() {
@@ -179,6 +266,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
// if we have already signed this key, dont bother doing it again
boolean alreadySigned = false;
+ /* todo: reconsider this at a later point when certs are in the db
@SuppressWarnings("unchecked")
Iterator<PGPSignature> itr = pubring.getPublicKey(mPubKeyId).getSignatures();
while (itr.hasNext()) {
@@ -188,6 +276,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
break;
}
}
+ */
if (!alreadySigned) {
/*
@@ -215,6 +304,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements
* kicks off the actual signing process on a background thread
*/
private void startSigning() {
+
+ // Bail out if there is not at least one user id selected
+ ArrayList<String> userIds = mUserIdsAdapter.getSelectedUserIds();
+ if(userIds.isEmpty()) {
+ Toast.makeText(CertifyKeyActivity.this, "No User IDs to sign selected!",
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+
// Send all information needed to service to sign key in other thread
Intent intent = new Intent(this, KeychainIntentService.class);
@@ -225,14 +323,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements
data.putLong(KeychainIntentService.CERTIFY_KEY_MASTER_KEY_ID, mMasterKeyId);
data.putLong(KeychainIntentService.CERTIFY_KEY_PUB_KEY_ID, mPubKeyId);
+ data.putStringArrayList(KeychainIntentService.CERTIFY_KEY_UIDS, userIds);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after signing is done in ApgService
+ // Message is received after signing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- R.string.progress_signing, ProgressDialog.STYLE_SPINNER) {
+ getString(R.string.progress_signing), ProgressDialog.STYLE_SPINNER) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -249,7 +348,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
finish();
}
}
- };
+ }
};
// Create a new Messenger for the communication back
@@ -281,11 +380,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after uploading is done in ApgService
+ // Message is received after uploading is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
+ getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -295,7 +394,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
setResult(RESULT_OK);
finish();
}
- };
+ }
};
// Create a new Messenger for the communication back
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index 42288ca37..9b3b00c19 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -17,14 +17,20 @@
package org.sufficientlysecure.keychain.ui;
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.util.regex.Matcher;
-
+import android.annotation.SuppressLint;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.animation.AnimationUtils;
+import android.widget.*;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Constants;
@@ -33,10 +39,10 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -48,27 +54,8 @@ import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
-import android.annotation.SuppressLint;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.animation.AnimationUtils;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.widget.ViewFlipper;
-
-import com.beardedhen.androidbootstrap.BootstrapButton;
-import com.devspark.appmsg.AppMsg;
+import java.io.*;
+import java.util.regex.Matcher;
@SuppressLint("NewApi")
public class DecryptActivity extends DrawerActivity {
@@ -364,7 +351,7 @@ public class DecryptActivity extends DrawerActivity {
}
} else {
Log.e(Constants.TAG,
- "Direct binary data without actual file in filesystem is not supported. Please use the Remote Service API!");
+ "Direct binary data without actual file in filesystem is not supported. Please use the Remote Service API!");
Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
.show();
// end activity
@@ -383,7 +370,7 @@ public class DecryptActivity extends DrawerActivity {
if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) {
filename = filename.substring(0, filename.length() - 4);
}
- mOutputFilename = Constants.path.APP_DIR + "/" + filename;
+ mOutputFilename = Constants.Path.APP_DIR + "/" + filename;
}
private void updateSource() {
@@ -456,8 +443,7 @@ public class DecryptActivity extends DrawerActivity {
getDecryptionKeyFromInputStream();
// if we need a symmetric passphrase or a passphrase to use a secret key ask for it
- if (mSecretKeyId == Id.key.symmetric
- || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) {
+ if (mAssumeSymmetricEncryption || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) {
showPassphraseDialog();
} else {
if (mDecryptTarget == Id.target.file) {
@@ -507,6 +493,7 @@ public class DecryptActivity extends DrawerActivity {
* TODO: Rework function, remove global variables
*/
private void getDecryptionKeyFromInputStream() {
+ mAssumeSymmetricEncryption = false;
InputStream inStream = null;
if (mContentUri != null) {
try {
@@ -546,7 +533,6 @@ public class DecryptActivity extends DrawerActivity {
if (mSecretKeyId == Id.key.none) {
throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
}
- mAssumeSymmetricEncryption = false;
} catch (NoAsymmetricEncryptionException e) {
if (inStream.markSupported()) {
inStream.reset();
@@ -559,6 +545,7 @@ public class DecryptActivity extends DrawerActivity {
mAssumeSymmetricEncryption = true;
}
} catch (Exception e) {
+ Log.e(Constants.TAG, "error while reading decryption key from input stream", e);
AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
AppMsg.STYLE_ALERT).show();
}
@@ -644,11 +631,11 @@ public class DecryptActivity extends DrawerActivity {
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after encrypting is done in ApgService
+ // Message is received after encrypting is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- R.string.progress_decrypting, ProgressDialog.STYLE_HORIZONTAL) {
+ getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -744,8 +731,6 @@ public class DecryptActivity extends DrawerActivity {
}
}
}
-
- ;
};
// Create a new Messenger for the communication back
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..f01e67449 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
@@ -17,30 +17,21 @@
package org.sufficientlysecure.keychain.ui;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
+import android.view.*;
+import android.widget.*;
import com.beardedhen.androidbootstrap.FontAwesomeText;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
public class DrawerActivity extends ActionBarActivity {
private DrawerLayout mDrawerLayout;
@@ -49,10 +40,8 @@ public class DrawerActivity extends ActionBarActivity {
private CharSequence mDrawerTitle;
private CharSequence mTitle;
+ private boolean mIsDrawerLocked = false;
- private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class,
- EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
- KeyListSecretActivity.class, RegisteredAppsListActivity.class };
private Class mSelectedItem;
private static final int MENU_ID_PREFERENCE = 222;
@@ -62,18 +51,29 @@ public class DrawerActivity extends ActionBarActivity {
mDrawerTitle = getString(R.string.app_name);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
+ ViewGroup viewGroup = (ViewGroup) findViewById(R.id.content_frame);
+ int leftMarginLoaded = ((ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams()).leftMargin;
+ int leftMarginInTablets = (int) getResources().getDimension(R.dimen.drawer_size);
+ int errorInMarginAllowed = 5;
+
+ // if the left margin of the loaded layout is close to the
+ // one used in tablets then set drawer as open and locked
+ if( Math.abs(leftMarginLoaded - leftMarginInTablets) < errorInMarginAllowed) {
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, mDrawerList);
+ mDrawerLayout.setScrimColor(Color.TRANSPARENT);
+ mIsDrawerLocked = true;
+ } else {
+ // set a custom shadow that overlays the main content when the drawer opens
+ mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
+ mIsDrawerLocked = false;
+ }
- // set a custom shadow that overlays the main content when the drawer
- // opens
- mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
-
- NavItem mItemIconTexts[] = new NavItem[] {
+ NavItem mItemIconTexts[] = new NavItem[]{
new NavItem("fa-user", getString(R.string.nav_contacts)),
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)) };
+ new NavItem("fa-android", getString(R.string.nav_apps))};
mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
mItemIconTexts));
@@ -81,32 +81,24 @@ public class DrawerActivity extends ActionBarActivity {
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// enable ActionBar app icon to behave as action to toggle nav drawer
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
+ // if the drawer is not locked
+ if ( !mIsDrawerLocked ) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+ }
// ActionBarDrawerToggle ties together the the proper interactions
// between the sliding drawer and the action bar app icon
mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
- mDrawerLayout, /* DrawerLayout object */
- R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
- R.string.drawer_open, /* "open drawer" description for accessibility */
- R.string.drawer_close /* "close drawer" description for accessibility */
+ mDrawerLayout, /* DrawerLayout object */
+ R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
+ R.string.drawer_open, /* "open drawer" description for accessibility */
+ R.string.drawer_close /* "close drawer" description for accessibility */
) {
public void onDrawerClosed(View view) {
getSupportActionBar().setTitle(mTitle);
- // creates call to onPrepareOptionsMenu()
- supportInvalidateOptionsMenu();
-
- // call intent activity if selected
- if(mSelectedItem != null) {
- finish();
- overridePendingTransition(0, 0);
- Intent intent = new Intent(DrawerActivity.this, mSelectedItem);
- startActivity(intent);
- // disable animation of activity start
- overridePendingTransition(0, 0);
- }
+ callIntentForDrawerItem(mSelectedItem);
}
public void onDrawerOpened(View drawerView) {
@@ -116,13 +108,40 @@ public class DrawerActivity extends ActionBarActivity {
supportInvalidateOptionsMenu();
}
};
- mDrawerLayout.setDrawerListener(mDrawerToggle);
+ if ( !mIsDrawerLocked ) {
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+ } else {
+ // If the drawer is locked open make it un-focusable
+ // so that it doesn't consume all the Back button presses
+ mDrawerLayout.setFocusableInTouchMode(false);
+ }
// if (savedInstanceState == null) {
// selectItem(0);
// }
}
+ /**
+ * Uses startActivity to call the Intent of the given class
+ * @param drawerItem the class of the drawer item you want to load. Based on Constants.DrawerItems.*
+ */
+ public void callIntentForDrawerItem(Class drawerItem) {
+ // creates call to onPrepareOptionsMenu()
+ supportInvalidateOptionsMenu();
+
+ // call intent activity if selected
+ if (drawerItem != null) {
+ finish();
+ overridePendingTransition(0, 0);
+
+ Intent intent = new Intent(this, drawerItem);
+ startActivity(intent);
+
+ // disable animation of activity start
+ overridePendingTransition(0, 0);
+ }
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences);
@@ -150,18 +169,18 @@ public class DrawerActivity extends ActionBarActivity {
}
switch (item.getItemId()) {
- case MENU_ID_PREFERENCE: {
- Intent intent = new Intent(this, PreferencesActivity.class);
- startActivity(intent);
- return true;
- }
- case MENU_ID_HELP: {
- Intent intent = new Intent(this, HelpActivity.class);
- startActivity(intent);
- return true;
- }
- default:
- return super.onOptionsItemSelected(item);
+ case MENU_ID_PREFERENCE: {
+ Intent intent = new Intent(this, PreferencesActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ case MENU_ID_HELP: {
+ Intent intent = new Intent(this, HelpActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ default:
+ return super.onOptionsItemSelected(item);
}
// Handle action buttons
@@ -193,10 +212,18 @@ public class DrawerActivity extends ActionBarActivity {
private void selectItem(int position) {
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
- // setTitle(mDrawerTitles[position]);
- mDrawerLayout.closeDrawer(mDrawerList);
// set selected class
- mSelectedItem = mItemsClass[position];
+ mSelectedItem = Constants.DrawerItems.ARRAY[position];
+
+ // setTitle(mDrawerTitles[position]);
+ // If drawer isn't locked just close the drawer and
+ // it will move to the selected item by itself (via drawer toggle listener)
+ if ( !mIsDrawerLocked ) {
+ mDrawerLayout.closeDrawer(mDrawerList);
+ // else move to the selected item yourself
+ } else {
+ callIntentForDrawerItem(mSelectedItem);
+ }
}
/**
@@ -229,15 +256,15 @@ public class DrawerActivity extends ActionBarActivity {
}
private class NavigationDrawerAdapter extends ArrayAdapter<NavItem> {
- Context context;
- int layoutResourceId;
- NavItem data[] = null;
+ Context mContext;
+ int mLayoutResourceId;
+ NavItem mData[] = null;
public NavigationDrawerAdapter(Context context, int layoutResourceId, NavItem[] data) {
super(context, layoutResourceId, data);
- this.layoutResourceId = layoutResourceId;
- this.context = context;
- this.data = data;
+ this.mLayoutResourceId = layoutResourceId;
+ this.mContext = context;
+ this.mData = data;
}
@Override
@@ -246,21 +273,21 @@ public class DrawerActivity extends ActionBarActivity {
NavItemHolder holder = null;
if (row == null) {
- LayoutInflater inflater = ((Activity) context).getLayoutInflater();
- row = inflater.inflate(layoutResourceId, parent, false);
+ LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
+ row = inflater.inflate(mLayoutResourceId, parent, false);
holder = new NavItemHolder();
- holder.img = (FontAwesomeText) row.findViewById(R.id.drawer_item_icon);
- holder.txtTitle = (TextView) row.findViewById(R.id.drawer_item_text);
+ holder.mImg = (FontAwesomeText) row.findViewById(R.id.drawer_item_icon);
+ holder.mTxtTitle = (TextView) row.findViewById(R.id.drawer_item_text);
row.setTag(holder);
} else {
holder = (NavItemHolder) row.getTag();
}
- NavItem item = data[position];
- holder.txtTitle.setText(item.title);
- holder.img.setIcon(item.icon);
+ NavItem item = mData[position];
+ holder.mTxtTitle.setText(item.title);
+ holder.mImg.setIcon(item.icon);
return row;
}
@@ -268,8 +295,8 @@ public class DrawerActivity extends ActionBarActivity {
}
static class NavItemHolder {
- FontAwesomeText img;
- TextView txtTitle;
+ FontAwesomeText mImg;
+ TextView mTxtTitle;
}
-} \ No newline at end of file
+}
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 0726897c3..b7fffc7ff 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
@@ -23,6 +23,25 @@ import java.util.List;
import java.util.Vector;
import org.spongycastle.bcpg.sig.KeyFlags;
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v7.app.ActionBarActivity;
+import android.view.*;
+import android.view.View.OnClickListener;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
@@ -32,6 +51,7 @@ import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@@ -49,31 +69,12 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import android.app.AlertDialog;
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.ActivityCompat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.LinearLayout;
-import android.widget.Toast;
-
-import com.beardedhen.androidbootstrap.BootstrapButton;
public class EditKeyActivity extends ActionBarActivity implements EditorListener {
@@ -113,7 +114,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
Vector<String> mUserIds;
Vector<PGPSecretKey> mKeys;
Vector<Integer> mKeysUsages;
- boolean masterCanSign = true;
+ boolean mMasterCanSign = true;
ExportHelper mExportHelper;
@@ -169,7 +170,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
/**
* Handle intent action to create new key
- *
+ *
* @param intent
*/
private void handleActionCreateKey(Intent intent) {
@@ -211,9 +212,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after generating is done in ApgService
+ // Message is received after generating is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
- this, R.string.progress_generating, ProgressDialog.STYLE_SPINNER, true,
+ this, getResources().getQuantityString(R.plurals.progress_generating, 1),
+ ProgressDialog.STYLE_HORIZONTAL, true,
new DialogInterface.OnCancelListener() {
@Override
@@ -227,7 +229,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
@Override
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -272,7 +274,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
/**
* Handle intent action to edit existing key
- *
+ *
* @param intent
*/
private void handleActionEditKey(Intent intent) {
@@ -283,12 +285,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
} 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.getSecretMasterKeyCanCertify(this, keyRingRowId);
+ mMasterCanSign = ProviderHelper.getMasterKeyCanCertify(this, mDataUri);
finallyEdit(masterKeyId);
}
}
@@ -350,11 +350,16 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
if (needsSaving()) {
Toast.makeText(this, R.string.error_save_first, Toast.LENGTH_LONG).show();
} else {
- mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
- + "/secexport.asc");
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
+ long[] ids = new long[]{masterKeyId};
+ mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC,
+ null);
+ return true;
}
return true;
- case R.id.menu_key_edit_delete: {
+ case R.id.menu_key_edit_delete:
+ long rowId= ProviderHelper.getRowId(this,mDataUri);
+ Uri convertUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@Override
@@ -363,12 +368,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
setResult(RESULT_CANCELED);
finish();
}
- }
- };
-
- mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler);
+ }};
+ mExportHelper.deleteKey(convertUri, returnHandler);
return true;
- }
+
case R.id.menu_key_edit_save:
saveClicked();
return true;
@@ -407,8 +410,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
}
mCurrentPassphrase = "";
-
buildLayout(false);
+
mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
if (!mIsPassPhraseSet) {
// check "no passphrase" checkbox and remove button
@@ -466,20 +469,23 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// find views
mChangePassphrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_passphrase);
mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
-
// Build layout based on given userIds and keys
+
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container);
+ if(mIsPassPhraseSet){
+ mChangePassphrase.setText(getString(R.string.btn_change_passphrase));
+ }
mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
mUserIdsView.setType(Id.type.user_id);
- mUserIdsView.setCanEdit(masterCanSign);
+ mUserIdsView.setCanEdit(mMasterCanSign);
mUserIdsView.setUserIds(mUserIds);
mUserIdsView.setEditorListener(this);
container.addView(mUserIdsView);
mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
mKeysView.setType(Id.type.key);
- mKeysView.setCanEdit(masterCanSign);
+ mKeysView.setCanEdit(mMasterCanSign);
mKeysView.setKeys(mKeys, mKeysUsages, newKeys);
mKeysView.setEditorListener(this);
container.addView(mKeysView);
@@ -602,17 +608,16 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// fill values for this action
Bundle data = new Bundle();
-
- data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, masterCanSign);
+ data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign);
data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after saving is done in ApgService
+ // Message is received after saving is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- R.string.progress_saving, ProgressDialog.STYLE_HORIZONTAL) {
+ getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -640,8 +645,9 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// start service with intent
startService(intent);
} catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
- Toast.LENGTH_SHORT).show();
+ Toast.LENGTH_SHORT).show();
}
}
@@ -679,7 +685,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
/**
* Returns user ids from the SectionView
- *
+ *
* @param userIdsView
* @return
*/
@@ -692,11 +698,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i);
String userId;
- try {
- userId = editor.getValue();
- } catch (UserIdEditor.InvalidEmailException e) {
- throw new PgpGeneralException(e.getMessage());
- }
+ userId = editor.getValue();
if (editor.isMainUserId()) {
userIds.add(0, userId);
@@ -719,7 +721,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
/**
* Returns keys from the SectionView
- *
+ *
* @param keysView
* @return
*/
@@ -742,7 +744,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
/**
* Returns usage selections of keys from the SectionView
- *
+ *
* @param keysView
* @return
*/
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index faac9b157..4f18f69d7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -17,9 +17,22 @@
package org.sufficientlysecure.keychain.ui;
-import java.io.File;
-import java.util.Vector;
-
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.*;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.beardedhen.androidbootstrap.FontAwesomeText;
+import com.devspark.appmsg.AppMsg;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
@@ -43,27 +56,8 @@ import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Choice;
import org.sufficientlysecure.keychain.util.Log;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.animation.AnimationUtils;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.Spinner;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.widget.ViewFlipper;
-
-import com.beardedhen.androidbootstrap.BootstrapButton;
-import com.devspark.appmsg.AppMsg;
+import java.io.File;
+import java.util.Vector;
public class EncryptActivity extends DrawerActivity {
@@ -108,6 +102,7 @@ public class EncryptActivity extends DrawerActivity {
private EditText mFilename = null;
private CheckBox mDeleteAfter = null;
+ private CheckBox mShareAfter = null;
private BootstrapButton mBrowse = null;
private String mInputFilename = null;
@@ -602,11 +597,11 @@ public class EncryptActivity extends DrawerActivity {
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after encrypting is done in ApgService
+ // Message is received after encrypting is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- R.string.progress_encrypting, ProgressDialog.STYLE_HORIZONTAL) {
+ getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -650,6 +645,15 @@ public class EncryptActivity extends DrawerActivity {
.newInstance(mInputFilename);
deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
}
+
+ if (mShareAfter.isChecked()) {
+ // Share encrypted file
+ Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
+ sendFileIntent.setType("*/*");
+ sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
+ startActivity(Intent.createChooser(sendFileIntent,
+ getString(R.string.title_send_file)));
+ }
break;
default:
@@ -659,8 +663,6 @@ public class EncryptActivity extends DrawerActivity {
}
}
}
-
- ;
};
// Create a new Messenger for the communication back
@@ -785,6 +787,11 @@ public class EncryptActivity extends DrawerActivity {
}
});
+
+
+
+
+
mFileCompression = (Spinner) findViewById(R.id.fileCompression);
Choice[] choices = new Choice[]{
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
@@ -794,7 +801,7 @@ public class EncryptActivity extends DrawerActivity {
new Choice(Id.choice.compression.zlib, "ZLIB ("
+ getString(R.string.compression_fast) + ")"),
new Choice(Id.choice.compression.bzip2, "BZIP2 ("
- + getString(R.string.compression_very_slow) + ")"),};
+ + getString(R.string.compression_very_slow) + ")"), };
ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(this,
android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
@@ -809,6 +816,7 @@ public class EncryptActivity extends DrawerActivity {
}
mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption);
+ mShareAfter = (CheckBox) findViewById(R.id.shareAfterEncryption);
mAsciiArmor = (CheckBox) findViewById(R.id.asciiArmour);
mAsciiArmor.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour());
@@ -947,8 +955,8 @@ public class EncryptActivity extends DrawerActivity {
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
- Bundle bundle = data.getExtras();
- mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
+ Uri uri_master_key = data.getData();
+ mSecretKeyId = Long.valueOf(uri_master_key.getLastPathSegment());
} else {
mSecretKeyId = Id.key.none;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
index 12d689274..a484b57de 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
@@ -17,11 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import org.sufficientlysecure.htmltextview.HtmlTextView;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -31,6 +26,10 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import org.sufficientlysecure.htmltextview.HtmlTextView;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
public class HelpAboutFragment extends Fragment {
@@ -55,7 +54,7 @@ public class HelpAboutFragment extends Fragment {
/**
* Get the current package version.
- *
+ *
* @return The current version.
*/
private String getVersion() {
@@ -73,4 +72,4 @@ public class HelpAboutFragment extends Fragment {
return result;
}
-} \ No newline at end of file
+}
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 9ccd7e088..32f37a0a5 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,24 +17,16 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.ArrayList;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
-
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
-import android.widget.TextView;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
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;
@@ -64,19 +56,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 ? true : false));
+ 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 ? true : false));
+ 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 ? true : false));
+ HelpHtmlFragment.class, changelogBundle, (selectedTab == 3));
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
- HelpAboutFragment.class, null, (selectedTab == 3 ? true : false));
+ HelpAboutFragment.class, null, (selectedTab == 4));
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java
index 3afb5bbe0..6b3c51b08 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java
@@ -17,8 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import org.sufficientlysecure.htmltextview.HtmlTextView;
-
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@@ -27,11 +25,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;
+import org.sufficientlysecure.htmltextview.HtmlTextView;
public class HelpHtmlFragment extends Fragment {
private Activity mActivity;
- private int htmlFile;
+ private int mHtmlFile;
public static final String ARG_HTML_FILE = "htmlFile";
@@ -52,8 +51,8 @@ public class HelpHtmlFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mActivity = getActivity();
-
- htmlFile = getArguments().getInt(ARG_HTML_FILE);
+
+ mHtmlFile = getArguments().getInt(ARG_HTML_FILE);
ScrollView scroller = new ScrollView(mActivity);
HtmlTextView text = new HtmlTextView(mActivity);
@@ -66,11 +65,11 @@ public class HelpHtmlFragment extends Fragment {
scroller.addView(text);
// load html from raw resource (Parsing handled by HtmlTextView library)
- text.setHtmlFromRawResource(getActivity(), htmlFile);
+ text.setHtmlFromRawResource(getActivity(), mHtmlFile);
// no flickering when clicking textview for Android < 4
text.setTextColor(getResources().getColor(android.R.color.black));
return scroller;
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 5ac421a44..834509f53 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -34,10 +34,8 @@ import android.support.v7.app.ActionBar;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@@ -161,7 +159,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
} else if (extras.containsKey(EXTRA_KEY_ID)) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
if (keyId != 0) {
- query = "0x" + PgpKeyHelper.convertKeyToHex(keyId);
+ query = PgpKeyHelper.convertKeyIdToHex(keyId);
}
} else if (extras.containsKey(EXTRA_FINGERPRINT)) {
String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT);
@@ -169,7 +167,8 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
query = "0x" + fingerprint;
}
} else {
- Log.e(Constants.TAG, "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or 'fingerprint' extra!");
+ Log.e(Constants.TAG,
+ "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or 'fingerprint' extra!");
return;
}
@@ -338,7 +337,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// } else {
// status.putString(
// EXTRA_ERROR,
- // "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key.");
+ // "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key.");
// }
// }
// } catch (QueryException e) {
@@ -360,51 +359,54 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// }
- // Message is received after importing is done in ApgService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- R.string.progress_importing, ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED);
- int updated = returnData
- .getInt(KeychainIntentService.RESULT_IMPORT_UPDATED);
- int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD);
- String toastMessage;
- if (added > 0 && updated > 0) {
- String addedStr = getResources().getQuantityString(
- R.plurals.keys_added_and_updated_1, added, added);
- String updatedStr = getResources().getQuantityString(
- R.plurals.keys_added_and_updated_2, updated, updated);
- toastMessage = addedStr + updatedStr;
- } else if (added > 0) {
- toastMessage = getResources().getQuantityString(R.plurals.keys_added,
- added, added);
- } else if (updated > 0) {
- toastMessage = getResources().getQuantityString(R.plurals.keys_updated,
- updated, updated);
- } else {
- toastMessage = getString(R.string.no_keys_added_or_updated);
- }
- AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO)
- .show();
- if (bad > 0) {
- BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad);
- badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
- }
- }
- }
- };
-
/**
* Import keys with mImportData
*/
public void importKeys() {
+ // Message is received after importing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
+ this,
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+
+ int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED);
+ int updated = returnData
+ .getInt(KeychainIntentService.RESULT_IMPORT_UPDATED);
+ int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD);
+ String toastMessage;
+ if (added > 0 && updated > 0) {
+ String addedStr = getResources().getQuantityString(
+ R.plurals.keys_added_and_updated_1, added, added);
+ String updatedStr = getResources().getQuantityString(
+ R.plurals.keys_added_and_updated_2, updated, updated);
+ toastMessage = addedStr + updatedStr;
+ } else if (added > 0) {
+ toastMessage = getResources().getQuantityString(R.plurals.keys_added,
+ added, added);
+ } else if (updated > 0) {
+ toastMessage = getResources().getQuantityString(R.plurals.keys_updated,
+ updated, updated);
+ } else {
+ toastMessage = getString(R.string.no_keys_added_or_updated);
+ }
+ AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO)
+ .show();
+ if (bad > 0) {
+ BadImportKeyDialogFragment badImportKeyDialogFragment =
+ BadImportKeyDialogFragment.newInstance(bad);
+ badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
+ }
+ }
+ }
+ };
+
if (mListFragment.getKeyBytes() != null || mListFragment.getDataUri() != null) {
Log.d(Constants.TAG, "importKeys started");
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
index 211fc1f44..3f0b4a46e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
@@ -17,17 +17,15 @@
package org.sufficientlysecure.keychain.ui;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
public class ImportKeysClipboardFragment extends Fragment {
@@ -60,8 +58,9 @@ public class ImportKeysClipboardFragment extends Fragment {
public void onClick(View v) {
CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
String sendText = "";
- if (clipboardText != null)
+ if (clipboardText != null) {
sendText = clipboardText.toString();
+ }
mImportActivity.loadCallback(sendText.getBytes(), null, null, null);
}
});
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
index 60b33c8a2..31d5f3fd0 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
@@ -17,11 +17,6 @@
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.FileHelper;
-
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
@@ -29,8 +24,11 @@ import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.FileHelper;
public class ImportKeysFileFragment extends Fragment {
private ImportKeysActivity mImportActivity;
@@ -62,7 +60,7 @@ public class ImportKeysFileFragment extends Fragment {
// open .asc or .gpg files
// setting it to text/plain prevents Cynaogenmod's file manager from selecting asc
// or gpg types!
- FileHelper.openFile(ImportKeysFileFragment.this, Constants.path.APP_DIR + "/",
+ FileHelper.openFile(ImportKeysFileFragment.this, Constants.Path.APP_DIR + "/",
"*/*", Id.request.filename);
}
});
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index 1118f0264..9e8506193 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -17,25 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import java.io.ByteArrayInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader;
-import org.sufficientlysecure.keychain.util.InputData;
-import org.sufficientlysecure.keychain.util.KeyServer;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
@@ -44,8 +25,21 @@ import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.view.View;
import android.widget.ListView;
-
import com.devspark.appmsg.AppMsg;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.ui.adapter.*;
+import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.KeyServer;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
public class ImportKeysListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
@@ -184,7 +178,8 @@ public class ImportKeysListFragment extends ListFragment implements
}
@Override
- public Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> onCreateLoader(int id, Bundle args) {
+ public Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>>
+ onCreateLoader(int id, Bundle args) {
switch (id) {
case LOADER_ID_BYTES: {
InputData inputData = getInputData(mKeyBytes, mDataUri);
@@ -219,27 +214,44 @@ public class ImportKeysListFragment extends ListFragment implements
} else {
setListShownNoAnimation(true);
}
+
+ Exception error = data.getError();
+
switch (loader.getId()) {
case LOADER_ID_BYTES:
+
+ if (error == null) {
+ // No error
+ } else if (error instanceof ImportKeysListLoader.FileHasNoContent) {
+ AppMsg.makeText(getActivity(), R.string.error_import_file_no_content,
+ AppMsg.STYLE_ALERT).show();
+ } else if (error instanceof ImportKeysListLoader.NonPgpPart) {
+ AppMsg.makeText(getActivity(),
+ ((ImportKeysListLoader.NonPgpPart) error).getCount() + " " + getResources().
+ getQuantityString(R.plurals.error_import_non_pgp_part,
+ ((ImportKeysListLoader.NonPgpPart) error).getCount()),
+ new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.confirm)).show();
+ } else {
+ AppMsg.makeText(getActivity(), R.string.error_generic_report_bug,
+ new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.alert)).show();
+ }
break;
case LOADER_ID_SERVER_QUERY:
- Exception error = data.getError();
-
- if(error == null){
+ if (error == null) {
AppMsg.makeText(
getActivity(), getResources().getQuantityString(R.plurals.keys_found,
mAdapter.getCount(), mAdapter.getCount()),
AppMsg.STYLE_INFO
).show();
- } else if(error instanceof KeyServer.InsufficientQuery){
+ } else if (error instanceof KeyServer.InsufficientQuery) {
AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query,
AppMsg.STYLE_ALERT).show();
- }else if(error instanceof KeyServer.QueryException){
+ } else if (error instanceof KeyServer.QueryException) {
AppMsg.makeText(getActivity(), R.string.error_keyserver_query,
AppMsg.STYLE_ALERT).show();
- }else if(error instanceof KeyServer.TooManyResponses){
+ } else if (error instanceof KeyServer.TooManyResponses) {
AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses,
AppMsg.STYLE_ALERT).show();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
index 83af8cf48..110647284 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
@@ -17,8 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import org.sufficientlysecure.keychain.R;
-
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@@ -26,8 +24,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.sufficientlysecure.keychain.R;
public class ImportKeysNFCFragment extends Fragment {
@@ -59,7 +57,7 @@ public class ImportKeysNFCFragment extends Fragment {
public void onClick(View v) {
// show nfc help
Intent intent = new Intent(getActivity(), HelpActivity.class);
- intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 1);
+ intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 2);
startActivityForResult(intent, 0);
}
});
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
index ee91b2434..10c0752b1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
@@ -17,14 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.ArrayList;
-import java.util.Locale;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -36,9 +28,15 @@ import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.google.zxing.integration.android.IntentResult;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
+import java.util.Locale;
public class ImportKeysQrCodeFragment extends Fragment {
@@ -94,45 +92,45 @@ public class ImportKeysQrCodeFragment extends Fragment {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode & 0xFFFF) {
- case IntentIntegratorSupportV4.REQUEST_CODE: {
- IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
- resultCode, data);
- if (scanResult != null && scanResult.getFormatName() != null) {
- String scannedContent = scanResult.getContents();
-
- Log.d(Constants.TAG, "scannedContent: " + scannedContent);
-
- // look if it's fingerprint only
- if (scannedContent.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) {
- importFingerprint(Uri.parse(scanResult.getContents()));
- return;
+ case IntentIntegratorSupportV4.REQUEST_CODE: {
+ IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
+ resultCode, data);
+ if (scanResult != null && scanResult.getFormatName() != null) {
+ String scannedContent = scanResult.getContents();
+
+ Log.d(Constants.TAG, "scannedContent: " + scannedContent);
+
+ // look if it's fingerprint only
+ if (scannedContent.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) {
+ importFingerprint(Uri.parse(scanResult.getContents()));
+ return;
+ }
+
+ // look if it is the whole key
+ String[] parts = scannedContent.split(",");
+ if (parts.length == 3) {
+ importParts(parts);
+ return;
+ }
+
+ // is this a full key encoded as qr code?
+ if (scannedContent.startsWith("-----BEGIN PGP")) {
+ mImportActivity.loadCallback(scannedContent.getBytes(), null, null, null);
+ return;
+ }
+
+ // fail...
+ Toast.makeText(getActivity(), R.string.import_qr_code_wrong, Toast.LENGTH_LONG)
+ .show();
}
- // look if it is the whole key
- String[] parts = scannedContent.split(",");
- if (parts.length == 3) {
- importParts(parts);
- return;
- }
-
- // is this a full key encoded as qr code?
- if (scannedContent.startsWith("-----BEGIN PGP")) {
- mImportActivity.loadCallback(scannedContent.getBytes(), null, null, null);
- return;
- }
-
- // fail...
- Toast.makeText(getActivity(), R.string.import_qr_code_wrong, Toast.LENGTH_LONG)
- .show();
+ break;
}
- break;
- }
-
- default:
- super.onActivityResult(requestCode, resultCode, data);
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
- break;
+ break;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
index d77015aa7..0b2fff64e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
@@ -17,11 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@@ -36,8 +31,11 @@ import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.util.Log;
public class ImportKeysServerFragment extends Fragment {
public static final String ARG_QUERY = "query";
@@ -96,7 +94,8 @@ public class ImportKeysServerFragment extends Fragment {
search(query, keyServer);
// close keyboard after pressing search
- InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ InputMethodManager imm =
+ (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mQueryEditText.getWindowToken(), 0);
}
});
@@ -110,7 +109,6 @@ public class ImportKeysServerFragment extends Fragment {
search(query, keyServer);
// Don't return true to let the keyboard close itself after pressing search
- // http://stackoverflow.com/questions/2342620/how-to-hide-keyboard-after-typing-in-edittext-in-android
return false;
}
return false;
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 e58ebe819..06df6f12d 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
@@ -17,17 +17,16 @@
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;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
-public class KeyListSecretActivity extends DrawerActivity {
+public class KeyListActivity extends DrawerActivity {
ExportHelper mExportHelper;
@@ -37,7 +36,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,34 +45,35 @@ 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
- + "/secexport.asc");
-
- 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:
+ callIntentForDrawerItem(Constants.DrawerItems.IMPORT_KEYS);
+
+ return true;
+ case R.id.menu_key_list_export:
+ mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB, null);
+
+ 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, null);
+
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
new file mode 100644
index 000000000..957c822d2
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -0,0 +1,686 @@
+/*
+ * 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
+ * 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 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.*;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.widget.SearchView;
+import android.text.TextUtils;
+import android.view.*;
+import android.view.View.OnClickListener;
+import android.view.animation.AnimationUtils;
+import android.widget.*;
+import android.widget.AbsListView.MultiChoiceModeListener;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+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.pgp.PgpKeyHelper;
+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.KeychainDatabase;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+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.StickyListHeadersAdapter;
+import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * 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 KeyListFragment extends Fragment
+ implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ private KeyListAdapter mAdapter;
+ private StickyListHeadersListView mStickyList;
+
+ // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
+ boolean mListShown;
+ View mProgressContainer;
+ View mListContainer;
+
+ private String mCurQuery;
+ private SearchView mSearchView;
+ // empty list layout
+ private BootstrapButton mButtonEmptyCreate;
+ private BootstrapButton mButtonEmptyImport;
+
+
+ /**
+ * Load custom layout with StickyListView from library
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View root = inflater.inflate(R.layout.key_list_fragment, container, false);
+
+ mStickyList = (StickyListHeadersListView) root.findViewById(R.id.key_list_list);
+ mStickyList.setOnItemClickListener(this);
+
+
+ // empty view
+ mButtonEmptyCreate = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_create);
+ mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(getActivity(), EditKeyActivity.class);
+ intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
+ intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
+ intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view
+ startActivityForResult(intent, 0);
+ }
+ });
+ mButtonEmptyImport = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_import);
+ mButtonEmptyImport.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
+ startActivityForResult(intent, 0);
+ }
+ });
+
+ // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
+ mListContainer = root.findViewById(R.id.key_list_list_container);
+ mProgressContainer = root.findViewById(R.id.key_list_progress_container);
+ mListShown = true;
+
+ return root;
+ }
+
+ /**
+ * Define Adapter and Loader on create of Activity
+ */
+ @SuppressLint("NewApi")
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mStickyList.setOnItemClickListener(this);
+ mStickyList.setAreHeadersSticky(true);
+ mStickyList.setDrawingListUnderStickyHeader(false);
+ mStickyList.setFastScrollEnabled(true);
+ try {
+ mStickyList.setFastScrollAlwaysVisible(true);
+ } catch (ApiLevelTooLowException e) {
+ }
+
+ // this view is made visible if no data is available
+ mStickyList.setEmptyView(getActivity().findViewById(R.id.key_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) {
+ mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ android.view.MenuInflater inflater = getActivity().getMenuInflater();
+ inflater.inflate(R.menu.key_list_multi, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+
+ // get IDs for checked positions as long array
+ long[] ids;
+
+ switch (item.getItemId()) {
+ case R.id.menu_key_list_multi_encrypt: {
+ ids = mAdapter.getCurrentSelectedMasterKeyIds();
+ encrypt(mode, ids);
+ break;
+ }
+ case R.id.menu_key_list_multi_delete: {
+ ids = mStickyList.getWrappedList().getCheckedItemIds();
+ showDeleteKeyDialog(mode, ids);
+ break;
+ }
+ case R.id.menu_key_list_multi_export: {
+ ids = mStickyList.getWrappedList().getCheckedItemIds();
+ long[] masterKeyIds = new long[2*ids.length];
+ ArrayList<Long> allPubRowIds =
+ ProviderHelper.getPublicKeyRingsRowIds(getActivity());
+ for (int i = 0; i < ids.length; i++) {
+ if (allPubRowIds.contains(ids[i])) {
+ masterKeyIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), ids[i]);
+ } else {
+ masterKeyIds[i] = ProviderHelper.getSecretMasterKeyId(getActivity(), ids[i]);
+ }
+ }
+ ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
+ mExportHelper
+ .showExportKeysDialog(masterKeyIds, Id.type.public_key,
+ Constants.Path.APP_DIR_FILE_PUB,
+ getString(R.string.also_export_secret_keys));
+ break;
+ }
+ case R.id.menu_key_list_multi_select_all: {
+ // select all
+ for (int i = 0; i < mStickyList.getCount(); i++) {
+ mStickyList.setItemChecked(i, true);
+ }
+ 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 = mStickyList.getCheckedItemCount();
+ String keysSelected = getResources().getQuantityString(
+ R.plurals.key_list_selected_keys, count, count);
+ mode.setTitle(keysSelected);
+ }
+
+ });
+ }
+
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
+
+ // NOTE: Not supported by StickyListHeader, but reimplemented here
+ // Start out with a progress indicator.
+ setListShown(false);
+
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new KeyListAdapter(getActivity(), null, Id.type.public_key);
+ mStickyList.setAdapter(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[]{
+ KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.TYPE,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.UserIds.USER_ID,
+ KeychainContract.Keys.IS_REVOKED
+ };
+
+ 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 =
+ // 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.buildUnifiedKeyRingsUri();
+ String where = null;
+ String whereArgs[] = null;
+ if (mCurQuery != null) {
+ where = KeychainContract.UserIds.USER_ID + " LIKE ?";
+ whereArgs = new String[]{"%" + mCurQuery + "%"};
+ }
+ // 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, where, whereArgs, SORT_ORDER);
+ }
+
+ @Override
+ 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.setSearchQuery(mCurQuery);
+ mAdapter.swapCursor(data);
+
+ mStickyList.setAdapter(mAdapter);
+
+ // NOTE: Not supported by StickyListHeader, but reimplemented here
+ // The list should now be shown.
+ if (isResumed()) {
+ setListShown(true);
+ } else {
+ setListShownNoAnimation(true);
+ }
+ }
+
+ @Override
+ 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 viewIntent = null;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
+ } else {
+ viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class);
+ }
+ viewIntent.setData(
+ KeychainContract
+ .KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
+ Long.toString(mAdapter.getMasterKeyId(position))));
+ startActivity(viewIntent);
+ }
+
+ @TargetApi(11)
+ 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, keyRingMasterKeyIds);
+ // used instead of startActivity set actionbar based on callingPackage
+ startActivityForResult(intent, 0);
+
+ mode.finish();
+ }
+
+ /**
+ * Show dialog to delete key
+ *
+ * @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() {
+ @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);
+
+ deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
+ }
+
+
+ @Override
+ public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+ // Get the searchview
+ MenuItem searchItem = menu.findItem(R.id.menu_key_list_search);
+ mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
+
+ // Execute this when searching
+ mSearchView.setOnQueryTextListener(this);
+
+ // Erase search result without focus
+ MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ mCurQuery = null;
+ mSearchView.setQuery("", true);
+ getLoaderManager().restartLoader(0, null, KeyListFragment.this);
+ return true;
+ }
+ });
+
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ // Called when the action bar search text has changed. Update
+ // the search filter, and restart the loader to do a new query
+ // with this filter.
+ mCurQuery = !TextUtils.isEmpty(s) ? s : null;
+ getLoaderManager().restartLoader(0, null, this);
+ return true;
+ }
+
+ // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
+ public void setListShown(boolean shown, boolean animate) {
+ if (mListShown == shown) {
+ return;
+ }
+ mListShown = shown;
+ if (shown) {
+ if (animate) {
+ mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_out));
+ mListContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_in));
+ }
+ mProgressContainer.setVisibility(View.GONE);
+ mListContainer.setVisibility(View.VISIBLE);
+ } else {
+ if (animate) {
+ mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_in));
+ mListContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_out));
+ }
+ mProgressContainer.setVisibility(View.VISIBLE);
+ mListContainer.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
+ public void setListShown(boolean shown) {
+ setListShown(shown, true);
+ }
+
+ // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
+ 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
+ View statusDivider = (View) view.findViewById(R.id.status_divider);
+ FrameLayout statusLayout = (FrameLayout) view.findViewById(R.id.status_layout);
+ 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
+ statusDivider.setVisibility(View.VISIBLE);
+ statusLayout.setVisibility(View.VISIBLE);
+ 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
+ statusDivider.setVisibility(View.GONE);
+ button.setVisibility(View.GONE);
+
+ boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
+ statusLayout.setVisibility(isRevoked ? View.VISIBLE : View.GONE);
+ 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.mText = (TextView) convertView.findViewById(R.id.stickylist_header_text);
+ holder.mCount = (TextView) convertView.findViewById(R.id.contacts_num);
+ convertView.setTag(holder);
+ } else {
+ holder = (HeaderViewHolder) convertView.getTag();
+ }
+
+ if (!mDataValid) {
+ // no data available at this point
+ Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
+ return convertView;
+ }
+
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("couldn't move cursor to position " + position);
+ }
+
+ if (mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) {
+ { // set contact count
+ int num = mCursor.getCount();
+ String contactsTotal = getResources().getQuantityString(R.plurals.n_contacts, num, num);
+ holder.mCount.setText(contactsTotal);
+ holder.mCount.setVisibility(View.VISIBLE);
+ }
+
+ holder.mText.setText(convertView.getResources().getString(R.string.my_keys));
+ return convertView;
+ }
+
+ // set header text as first char in user id
+ String userId = mCursor.getString(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.mText.setText(headerText);
+ holder.mCount.setVisibility(View.GONE);
+ return convertView;
+ }
+
+ /**
+ * Header IDs should be static, position=1 should always return the same Id that is.
+ */
+ @Override
+ public long getHeaderId(int position) {
+ if (!mDataValid) {
+ // no data available at this point
+ Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
+ return -1;
+ }
+
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("couldn't move cursor to position " + position);
+ }
+
+ // early breakout: all secret keys are assigned id 0
+ if (mCursor.getInt(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 mText;
+ TextView mCount;
+ }
+
+ /**
+ * -------------------------- 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 4521786f7..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
+++ /dev/null
@@ -1,71 +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
- + "/pubexport.asc");
-
- return true;
- 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/KeyListPublicFragment.java
deleted file mode 100644
index f2cb8a265..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ /dev/null
@@ -1,382 +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 java.util.ArrayList;
-import java.util.Set;
-
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
-
-import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
-import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
-
-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.Fragment;
-import android.support.v4.app.LoaderManager;
-import android.support.v4.content.CursorLoader;
-import android.support.v4.content.Loader;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.SearchView;
-import android.text.TextUtils;
-import android.view.ActionMode;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.AbsListView.MultiChoiceModeListener;
-import android.widget.AdapterView;
-import android.widget.ListView;
-import android.widget.Toast;
-
-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,
- LoaderManager.LoaderCallbacks<Cursor> {
-
- private KeyListPublicAdapter mAdapter;
- private StickyListHeadersListView mStickyList;
- private String mCurQuery;
- private SearchView mSearchView;
- // empty list layout
- private BootstrapButton mButtonEmptyCreate;
- private BootstrapButton mButtonEmptyImport;
-
-
- /**
- * Load custom layout with StickyListView from library
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.key_list_public_fragment, container, false);
- setHasOptionsMenu(true);
- mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create);
- mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(getActivity(), EditKeyActivity.class);
- intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
- intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
- intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view
- startActivityForResult(intent, 0);
- }
- });
-
- mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import);
- mButtonEmptyImport.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
- startActivityForResult(intent, 0);
- }
- });
-
- return view;
- }
-
- /**
- * Define Adapter and Loader on create of Activity
- */
- @SuppressLint("NewApi")
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
-
- mStickyList.setOnItemClickListener(this);
- mStickyList.setAreHeadersSticky(true);
- mStickyList.setDrawingListUnderStickyHeader(false);
- mStickyList.setFastScrollEnabled(true);
- try {
- mStickyList.setFastScrollAlwaysVisible(true);
- } catch (ApiLevelTooLowException e) {
- }
-
- // this view is made visible if no data is available
- mStickyList.setEmptyView(getActivity().findViewById(R.id.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) {
- mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
- mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- android.view.MenuInflater inflater = getActivity().getMenuInflater();
- inflater.inflate(R.menu.key_list_public_multi, menu);
- return true;
- }
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- Set<Integer> positions = mAdapter.getCurrentCheckedPosition();
-
- // get IDs for checked positions as long array
- long[] ids = new long[positions.size()];
- int i = 0;
- for (int pos : positions) {
- ids[i] = mAdapter.getItemId(pos);
- i++;
- }
-
- switch (item.getItemId()) {
- case R.id.menu_key_list_public_multi_encrypt: {
- encrypt(mode, ids);
- break;
- }
- 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
- int localCount = mStickyList.getCount();
- for(int k = 0; k < localCount; k++) {
- mStickyList.setItemChecked(k, true);
- }
- 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 = mAdapter.getCurrentCheckedPosition().size();
- String keysSelected = getResources().getQuantityString(
- R.plurals.key_list_selected_keys, count, count);
- mode.setTitle(keysSelected);
- }
-
- });
- }
-
- // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
- // Start out with a progress indicator.
- // 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);
- mStickyList.setAdapter(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[]{
- KeychainContract.KeyRings._ID,
- KeychainContract.KeyRings.MASTER_KEY_ID,
- KeychainContract.UserIds.USER_ID,
- KeychainContract.Keys.IS_REVOKED
- };
-
- static final int USER_ID_INDEX = 2;
-
- static final String SORT_ORDER = 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();
- String where = null;
- String whereArgs[] = null;
- if(mCurQuery != null){
- where = KeychainContract.UserIds.USER_ID + " LIKE ?";
- whereArgs = new String[]{mCurQuery+"%"};
- }
- // 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, where, whereArgs, SORT_ORDER);
- }
-
- @Override
- 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);
-
- mStickyList.setAdapter(mAdapter);
-
- // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
- // The list should now be shown.
- // if (isResumed()) {
- // setListShown(true);
- // } else {
- // setListShownNoAnimation(true);
- // }
- }
-
- @Override
- 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 viewIntent = null;
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
- viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
- } else {
- viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class);
- }
- viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id)));
- 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]);
- }
-
- Intent intent = new Intent(getActivity(), EncryptActivity.class);
- intent.setAction(EncryptActivity.ACTION_ENCRYPT);
- intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds);
- // used instead of startActivity set actionbar based on callingPackage
- startActivityForResult(intent, 0);
-
- mode.finish();
- }
-
- /**
- * 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) {
- Bundle returnData = message.getData();
- if (returnData != null
- && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) {
- ArrayList<String> notDeleted =
- returnData.getStringArrayList(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED);
- String notDeletedMsg = "";
- for (String userId : notDeleted) {
- notDeletedMsg += userId + "\n";
- }
- Toast.makeText(getActivity(), getString(R.string.error_can_not_delete_contacts, notDeletedMsg)
- + getResources().getQuantityString(R.plurals.error_can_not_delete_info, notDeleted.size()),
- Toast.LENGTH_LONG).show();
-
- mode.finish();
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- keyRingRowIds, Id.type.public_key);
-
- deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
- }
-
-
- @Override
- public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
-
- // Get the searchview
- MenuItem searchItem = menu.findItem(R.id.menu_key_list_public_search);
- mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
-
- // Execute this when searching
- mSearchView.setOnQueryTextListener(this);
-
- super.onCreateOptionsMenu(menu, inflater);
-
- }
-
- @Override
- public boolean onQueryTextSubmit(String s) {
- return true;
- }
-
- @Override
- public boolean onQueryTextChange(String s) {
- // Called when the action bar search text has changed. Update
- // the search filter, and restart the loader to do a new query
- // with this filter.
- String newQuery = !TextUtils.isEmpty(s) ? s : null;
- mCurQuery = newQuery;
- getLoaderManager().restartLoader(0, null, this);
- return true;
- }
-}
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 eaac6d8b1..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
+++ /dev/null
@@ -1,238 +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 java.util.ArrayList;
-import java.util.Set;
-
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-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.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;
-import android.widget.Toast;
-
-public class KeyListSecretFragment extends ListFragment implements
- LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener {
-
- private KeyListSecretActivity mKeyListSecretActivity;
- private KeyListSecretAdapter mAdapter;
-
- /**
- * Define Adapter and Loader on create of Activity
- */
- @SuppressLint("NewApi")
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- mKeyListSecretActivity = (KeyListSecretActivity) getActivity();
-
- 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) {
- Set<Integer> positions = mAdapter.getCurrentCheckedPosition();
-
- // get IDs for checked positions as long array
- long[] ids = new long[positions.size()];
- int i = 0;
- for (int pos : positions) {
- ids[i] = mAdapter.getItemId(pos);
- i++;
- }
-
- 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
- int localCount = getListView().getCount();
- for(int k = 0; k < localCount; k++) {
- getListView().setItemChecked(k, true);
- }
- 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(mKeyListSecretActivity, 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(mKeyListSecretActivity, 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/PreferencesActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
index af20b499b..04179cb80 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -16,6 +16,11 @@
package org.sufficientlysecure.keychain.ui;
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.*;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
@@ -24,25 +29,13 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.PreferenceActivity;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
-import android.support.v7.app.ActionBarActivity;
-
import java.util.List;
@SuppressLint("NewApi")
public class PreferencesActivity extends PreferenceActivity {
- public final static String ACTION_PREFS_GEN = "org.sufficientlysecure.keychain.ui.PREFS_GEN";
- public final static String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
+ public static final String ACTION_PREFS_GEN = "org.sufficientlysecure.keychain.ui.PREFS_GEN";
+ public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
private PreferenceScreen mKeyServerPreference = null;
private static Preferences mPreferences;
@@ -57,16 +50,15 @@ public class PreferencesActivity extends PreferenceActivity {
// actionBar.setDisplayHomeAsUpEnabled(false);
// actionBar.setHomeButtonEnabled(false);
- //addPreferencesFromResource(R.xml.preferences);
String action = getIntent().getAction();
if (action != null && action.equals(ACTION_PREFS_GEN)) {
addPreferencesFromResource(R.xml.gen_preferences);
initializePassPassPhraceCacheTtl(
- (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL));
+ (IntegerListPreference) findPreference(Constants.Pref.PASS_PHRASE_CACHE_TTL));
- mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS);
+ mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS);
String servers[] = mPreferences.getKeyServers();
mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers,
servers.length, servers.length));
@@ -86,11 +78,11 @@ public class PreferencesActivity extends PreferenceActivity {
addPreferencesFromResource(R.xml.adv_preferences);
initializeEncryptionAlgorithm(
- (IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM));
+ (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM));
- int[] valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip,
+ int[] valueIds = new int[]{Id.choice.compression.none, Id.choice.compression.zip,
Id.choice.compression.zlib, Id.choice.compression.bzip2, };
- String[] entries = new String[] {
+ String[] entries = new String[]{
getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
"ZIP (" + getString(R.string.compression_fast) + ")",
"ZLIB (" + getString(R.string.compression_fast) + ")",
@@ -101,20 +93,22 @@ public class PreferencesActivity extends PreferenceActivity {
}
initializeHashAlgorithm(
- (IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM),
- valueIds, entries, values);
+ (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_HASH_ALGORITHM),
+ valueIds, entries, values);
initializeMessageCompression(
- (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION),
- valueIds, entries, values);
+ (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION),
+ valueIds, entries, values);
initializeFileCompression(
- (IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION),
- entries, values);
+ (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION),
+ entries, values);
- initializeAsciiArmour((CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR));
+ initializeAsciiArmour(
+ (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOUR));
- initializeForceV3Signatures((CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES));
+ initializeForceV3Signatures(
+ (CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// Load the legacy preferences headers
@@ -151,7 +145,9 @@ public class PreferencesActivity extends PreferenceActivity {
loadHeadersFromResource(R.xml.preference_headers, target);
}
- /** This fragment shows the general preferences in android 3.0+ */
+ /**
+ * This fragment shows the general preferences in android 3.0+
+ */
public static class GeneralPrefsFragment extends PreferenceFragment {
private PreferenceScreen mKeyServerPreference = null;
@@ -164,9 +160,9 @@ public class PreferencesActivity extends PreferenceActivity {
addPreferencesFromResource(R.xml.gen_preferences);
initializePassPassPhraceCacheTtl(
- (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL));
+ (IntegerListPreference) findPreference(Constants.Pref.PASS_PHRASE_CACHE_TTL));
- mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS);
+ mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS);
String servers[] = mPreferences.getKeyServers();
mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers,
servers.length, servers.length));
@@ -206,7 +202,9 @@ public class PreferencesActivity extends PreferenceActivity {
}
}
- /** This fragment shows the advanced preferences in android 3.0+ */
+ /**
+ * This fragment shows the advanced preferences in android 3.0+
+ */
public static class AdvancedPrefsFragment extends PreferenceFragment {
@Override
@@ -217,11 +215,11 @@ public class PreferencesActivity extends PreferenceActivity {
addPreferencesFromResource(R.xml.adv_preferences);
initializeEncryptionAlgorithm(
- (IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM));
+ (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM));
- int[] valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip,
+ int[] valueIds = new int[]{Id.choice.compression.none, Id.choice.compression.zip,
Id.choice.compression.zlib, Id.choice.compression.bzip2, };
- String[] entries = new String[] {
+ String[] entries = new String[]{
getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
"ZIP (" + getString(R.string.compression_fast) + ")",
"ZLIB (" + getString(R.string.compression_fast) + ")",
@@ -232,24 +230,26 @@ public class PreferencesActivity extends PreferenceActivity {
}
initializeHashAlgorithm(
- (IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM),
- valueIds, entries, values);
+ (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_HASH_ALGORITHM),
+ valueIds, entries, values);
initializeMessageCompression(
- (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION),
- valueIds, entries, values);
+ (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION),
+ valueIds, entries, values);
initializeFileCompression(
- (IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION),
+ (IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION),
entries, values);
- initializeAsciiArmour((CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR));
+ initializeAsciiArmour(
+ (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOUR));
- initializeForceV3Signatures((CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES));
+ initializeForceV3Signatures(
+ (CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
}
}
- protected boolean isValidFragment (String fragmentName) {
+ protected boolean isValidFragment(String fragmentName) {
return AdvancedPrefsFragment.class.getName().equals(fragmentName)
|| GeneralPrefsFragment.class.getName().equals(fragmentName)
|| super.isValidFragment(fragmentName);
@@ -270,11 +270,11 @@ public class PreferencesActivity extends PreferenceActivity {
}
private static void initializeEncryptionAlgorithm(final IntegerListPreference mEncryptionAlgorithm) {
- int valueIds[] = { PGPEncryptedData.AES_128, PGPEncryptedData.AES_192,
+ int valueIds[] = {PGPEncryptedData.AES_128, PGPEncryptedData.AES_192,
PGPEncryptedData.AES_256, PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH,
PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES,
PGPEncryptedData.IDEA, };
- String entries[] = { "AES-128", "AES-192", "AES-256", "Blowfish", "Twofish", "CAST5",
+ String entries[] = {"AES-128", "AES-192", "AES-256", "Blowfish", "Twofish", "CAST5",
"DES", "Triple DES", "IDEA", };
String values[] = new String[valueIds.length];
for (int i = 0; i < values.length; ++i) {
@@ -298,10 +298,10 @@ public class PreferencesActivity extends PreferenceActivity {
private static void initializeHashAlgorithm
(final IntegerListPreference mHashAlgorithm, int[] valueIds, String[] entries, String[] values) {
- valueIds = new int[] { HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160,
+ valueIds = new int[]{HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160,
HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256,
HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512, };
- entries = new String[] { "MD5", "RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384",
+ entries = new String[]{"MD5", "RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384",
"SHA-512", };
values = new String[valueIds.length];
for (int i = 0; i < values.length; ++i) {
@@ -321,8 +321,9 @@ public class PreferencesActivity extends PreferenceActivity {
});
}
- private static void initializeMessageCompression
- (final IntegerListPreference mMessageCompression, int[] valueIds, String[] entries, String[] values) {
+ private static void initializeMessageCompression(
+ final IntegerListPreference mMessageCompression,
+ int[] valueIds, String[] entries, String[] values) {
mMessageCompression.setEntries(entries);
mMessageCompression.setEntryValues(values);
mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
@@ -377,4 +378,4 @@ public class PreferencesActivity extends PreferenceActivity {
}
});
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
index b39b93f52..719378274 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
@@ -16,14 +16,6 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.Vector;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-import org.sufficientlysecure.keychain.ui.widget.Editor;
-import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
-import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor;
-
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -33,6 +25,13 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.TextView;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ActionBarHelper;
+import org.sufficientlysecure.keychain.ui.widget.Editor;
+import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
+import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor;
+
+import java.util.Vector;
public class PreferencesKeyServerActivity extends ActionBarActivity implements OnClickListener,
EditorListener {
@@ -50,20 +49,21 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
super.onCreate(savedInstanceState);
// Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
+ ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {
// ok
okClicked();
}
- }, R.string.btn_do_not_save, new View.OnClickListener() {
+ }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
@Override
public void onClick(View v) {
// cancel
cancelClicked();
}
- });
+ }
+ );
setContentView(R.layout.key_server_preference);
@@ -81,11 +81,11 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
Intent intent = getIntent();
String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS);
if (servers != null) {
- for (int i = 0; i < servers.length; ++i) {
+ for (String serv : servers) {
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(
R.layout.key_server_editor, mEditors, false);
view.setEditorListener(this);
- view.setValue(servers[i]);
+ view.setValue(serv);
mEditors.addView(view);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java
index 86ae0073f..874703704 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java
@@ -17,14 +17,13 @@
package org.sufficientlysecure.keychain.ui;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ActionBarHelper;
public class SelectPublicKeyActivity extends ActionBarActivity {
@@ -39,27 +38,28 @@ public class SelectPublicKeyActivity extends ActionBarActivity {
SelectPublicKeyFragment mSelectFragment;
- long selectedMasterKeyIds[];
+ long mSelectedMasterKeyIds[];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
+ ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
new View.OnClickListener() {
@Override
public void onClick(View v) {
// ok
okClicked();
}
- }, R.string.btn_do_not_save, new View.OnClickListener() {
+ }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
@Override
public void onClick(View v) {
// cancel
cancelClicked();
}
- });
+ }
+ );
setContentView(R.layout.select_public_key_activity);
@@ -79,7 +79,7 @@ public class SelectPublicKeyActivity extends ActionBarActivity {
}
// Create an instance of the fragment
- mSelectFragment = SelectPublicKeyFragment.newInstance(selectedMasterKeyIds);
+ mSelectFragment = SelectPublicKeyFragment.newInstance(mSelectedMasterKeyIds);
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
@@ -124,7 +124,7 @@ public class SelectPublicKeyActivity extends ActionBarActivity {
// }
// preselected master keys
- selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
+ mSelectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
}
private void cancelClicked() {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
index a43c84cf4..6ab9f1c6e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
@@ -17,20 +17,7 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.Date;
-import java.util.Vector;
-
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
-import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
-
-import android.app.Activity;
+import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
@@ -41,20 +28,40 @@ import android.support.v4.content.Loader;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
-import android.widget.EditText;
-import android.widget.ListView;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.*;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
+
+import java.util.Date;
+import java.util.Vector;
public class SelectPublicKeyFragment extends ListFragmentWorkaround implements TextWatcher,
LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_PRESELECTED_KEY_IDS = "preselected_key_ids";
- private Activity mActivity;
private SelectKeyCursorAdapter mAdapter;
- private ListView mListView;
private EditText mSearchView;
private long mSelectedMasterKeyIds[];
private String mCurQuery;
+ // copied from ListFragment
+ static final int INTERNAL_EMPTY_ID = 0x00ff0001;
+ static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002;
+ static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003;
+ // added for search view
+ static final int SEARCH_ID = 0x00ff0004;
+
/**
* Creates new instance of this fragment
*/
@@ -72,27 +79,101 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mSearchView = (EditText)getActivity().findViewById(R.id.select_public_key_search);
- mSearchView.addTextChangedListener(this);
mSelectedMasterKeyIds = getArguments().getLongArray(ARG_PRESELECTED_KEY_IDS);
}
/**
+ * Copied from ListFragment and added EditText for search on top of list.
+ * We do not use a custom layout here, because this breaks the progress bar functionality
+ * of ListFragment.
+ *
+ * @param inflater
+ * @param container
+ * @param savedInstanceState
+ * @return
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final Context context = getActivity();
+
+ FrameLayout root = new FrameLayout(context);
+
+ // ------------------------------------------------------------------
+
+ LinearLayout pframe = new LinearLayout(context);
+ pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID);
+ pframe.setOrientation(LinearLayout.VERTICAL);
+ pframe.setVisibility(View.GONE);
+ pframe.setGravity(Gravity.CENTER);
+
+ ProgressBar progress = new ProgressBar(context, null,
+ android.R.attr.progressBarStyleLarge);
+ pframe.addView(progress, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ root.addView(pframe, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
+
+ // ------------------------------------------------------------------
+
+ FrameLayout lframe = new FrameLayout(context);
+ lframe.setId(INTERNAL_LIST_CONTAINER_ID);
+
+ TextView tv = new TextView(getActivity());
+ tv.setId(INTERNAL_EMPTY_ID);
+ tv.setGravity(Gravity.CENTER);
+ lframe.addView(tv, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
+
+ // Added for search view: linearLayout, mSearchView
+ LinearLayout linearLayout = new LinearLayout(context);
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+
+ mSearchView = new EditText(context);
+ mSearchView.setId(SEARCH_ID);
+ mSearchView.setHint(R.string.menu_search);
+ mSearchView.setCompoundDrawablesWithIntrinsicBounds(
+ getResources().getDrawable(R.drawable.ic_action_search), null, null, null);
+
+ linearLayout.addView(mSearchView, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ ListView lv = new ListView(getActivity());
+ lv.setId(android.R.id.list);
+ lv.setDrawSelectorOnTop(false);
+ linearLayout.addView(lv, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
+
+ lframe.addView(linearLayout, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
+
+ root.addView(lframe, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
+
+ // ------------------------------------------------------------------
+
+ root.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
+
+ return root;
+ }
+
+ /**
* Define Adapter and Loader on create of Activity
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mActivity = getActivity();
- mListView = getListView();
+ getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
// 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));
- mAdapter = new SelectKeyCursorAdapter(mActivity, null, 0, mListView, Id.type.public_key);
+ mSearchView.addTextChangedListener(this);
+
+ mAdapter = new SelectKeyCursorAdapter(getActivity(), null, 0, getListView(), Id.type.public_key);
setListAdapter(mAdapter);
@@ -106,16 +187,16 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
/**
* Selects items based on master key ids in list view
- *
+ *
* @param masterKeyIds
*/
private void preselectMasterKeyIds(long[] masterKeyIds) {
if (masterKeyIds != null) {
- for (int i = 0; i < mListView.getCount(); ++i) {
+ for (int i = 0; i < getListView().getCount(); ++i) {
long keyId = mAdapter.getMasterKeyId(i);
- for (int j = 0; j < masterKeyIds.length; ++j) {
- if (keyId == masterKeyIds[j]) {
- mListView.setItemChecked(i, true);
+ for (long masterKeyId : masterKeyIds) {
+ if (keyId == masterKeyId) {
+ getListView().setItemChecked(i, true);
break;
}
}
@@ -125,15 +206,15 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
/**
* Returns all selected master key ids
- *
+ *
* @return
*/
public long[] getSelectedMasterKeyIds() {
// mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key
// ids!
Vector<Long> vector = new Vector<Long>();
- for (int i = 0; i < mListView.getCount(); ++i) {
- if (mListView.isItemChecked(i)) {
+ for (int i = 0; i < getListView().getCount(); ++i) {
+ if (getListView().isItemChecked(i)) {
vector.add(mAdapter.getMasterKeyId(i));
}
}
@@ -149,13 +230,13 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
/**
* Returns all selected user ids
- *
+ *
* @return
*/
public String[] getSelectedUserIds() {
Vector<String> userIds = new Vector<String>();
- for (int i = 0; i < mListView.getCount(); ++i) {
- if (mListView.isItemChecked(i)) {
+ for (int i = 0; i < getListView().getCount(); ++i) {
+ if (getListView().isItemChecked(i)) {
userIds.add((String) mAdapter.getUserId(i));
}
}
@@ -173,7 +254,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
// These are the rows that we will retrieve.
long now = new Date().getTime() / 1000;
- String[] projection = new String[] {
+ String[] projection = new String[]{
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
UserIds.USER_ID,
@@ -204,22 +285,6 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
inMasterKeyList += ")";
}
- // if (searchString != null && searchString.trim().length() > 0) {
- // String[] chunks = searchString.trim().split(" +");
- // qb.appendWhere("(EXISTS (SELECT tmp." + UserIds._ID + " FROM " + UserIds.TABLE_NAME
- // + " AS tmp WHERE " + "tmp." + UserIds.KEY_ID + " = " + Keys.TABLE_NAME + "."
- // + Keys._ID);
- // for (int i = 0; i < chunks.length; ++i) {
- // qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
- // qb.appendWhereEscapeString("%" + chunks[i] + "%");
- // }
- // qb.appendWhere("))");
- //
- // if (inIdList != null) {
- // qb.appendWhere(" OR (" + inIdList + ")");
- // }
- // }
-
String orderBy = UserIds.USER_ID + " ASC";
if (inMasterKeyList != null) {
// sort by selected master keys
@@ -227,9 +292,9 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
}
String where = null;
String whereArgs[] = null;
- if(mCurQuery != null){
+ if (mCurQuery != null) {
where = UserIds.USER_ID + " LIKE ?";
- whereArgs = new String[]{mCurQuery+"%"};
+ whereArgs = new String[]{"%" + mCurQuery + "%"};
}
// Now create and return a CursorLoader that will take care of
@@ -241,6 +306,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
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.setSearchQuery(mCurQuery);
mAdapter.swapCursor(data);
// The list should now be shown.
@@ -274,8 +340,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
@Override
public void afterTextChanged(Editable editable) {
- String newQuery = !TextUtils.isEmpty(editable.toString()) ? editable.toString() : null;
- mCurQuery = newQuery;
+ mCurQuery = !TextUtils.isEmpty(editable.toString()) ? editable.toString() : null;
getLoaderManager().restartLoader(0, null, this);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
index cd3b2cf78..0ff88d97c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
@@ -1,43 +1,37 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * 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.R;
-
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
-import android.view.Menu;
-public class SelectSecretKeyActivity extends ActionBarActivity {
+import org.sufficientlysecure.keychain.R;
- // Actions for internal use only:
- public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX
- + "SELECT_SECRET_KEYRING";
+public class SelectSecretKeyActivity extends ActionBarActivity {
public static final String EXTRA_FILTER_CERTIFY = "filter_certify";
public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id";
- public static final String RESULT_EXTRA_USER_ID = "user_id";
- private boolean mFilterCertify = false;
+ private boolean mFilterCertify;
private SelectSecretKeyFragment mSelectFragment;
@Override
@@ -51,23 +45,8 @@ public class SelectSecretKeyActivity extends ActionBarActivity {
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setHomeButtonEnabled(false);
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
-
- // TODO: reimplement!
- // mFilterLayout = findViewById(R.id.layout_filter);
- // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
- // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
- //
- // mClearFilterButton.setOnClickListener(new OnClickListener() {
- // public void onClick(View v) {
- // handleIntent(new Intent());
- // }
- // });
-
mFilterCertify = getIntent().getBooleanExtra(EXTRA_FILTER_CERTIFY, false);
- handleIntent(getIntent());
-
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.select_secret_key_fragment_container) != null) {
@@ -90,49 +69,15 @@ public class SelectSecretKeyActivity extends ActionBarActivity {
/**
* This is executed by SelectSecretKeyFragment after clicking on an item
- *
- * @param masterKeyId
- * @param userId
+ *
+ * @param selectedUri
*/
- public void afterListSelection(long masterKeyId, String userId) {
+ public void afterListSelection(Uri selectedUri) {
Intent data = new Intent();
- data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId);
- data.putExtra(RESULT_EXTRA_USER_ID, (String) userId);
+ data.setData(selectedUri);
+
setResult(RESULT_OK, data);
finish();
}
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- // TODO: reimplement!
-
- // String searchString = null;
- // if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
- // searchString = intent.getStringExtra(SearchManager.QUERY);
- // if (searchString != null && searchString.trim().length() == 0) {
- // searchString = null;
- // }
- // }
-
- // if (searchString == null) {
- // mFilterLayout.setVisibility(View.GONE);
- // } else {
- // mFilterLayout.setVisibility(View.VISIBLE);
- // mFilterInfo.setText(getString(R.string.filterInfo, searchString));
- // }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // TODO: reimplement!
- // menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon(
- // android.R.drawable.ic_menu_search);
- return true;
- }
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
index 8ee2d381e..2efa7d33a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.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>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,17 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.Date;
-
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
-import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
-
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -40,15 +29,26 @@ import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
+
+import java.util.Date;
+
public class SelectSecretKeyFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
private SelectSecretKeyActivity mActivity;
private SelectKeyCursorAdapter mAdapter;
private ListView mListView;
-
+
private boolean mFilterCertify;
-
+
private static final String ARG_FILTER_CERTIFY = "filter_certify";
/**
@@ -56,10 +56,9 @@ public class SelectSecretKeyFragment extends ListFragment implements
*/
public static SelectSecretKeyFragment newInstance(boolean filterCertify) {
SelectSecretKeyFragment frag = new SelectSecretKeyFragment();
- Bundle args = new Bundle();
+ Bundle args = new Bundle();
args.putBoolean(ARG_FILTER_CERTIFY, filterCertify);
-
frag.setArguments(args);
return frag;
@@ -86,10 +85,10 @@ public class SelectSecretKeyFragment extends ListFragment implements
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
long masterKeyId = mAdapter.getMasterKeyId(position);
- String userId = mAdapter.getUserId(position);
+ Uri result = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId));
// return data to activity, which results in finishing it
- mActivity.afterListSelection(masterKeyId, userId);
+ mActivity.afterListSelection(result);
}
});
@@ -122,7 +121,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
// These are the rows that we will retrieve.
long now = new Date().getTime() / 1000;
- String[] projection = new String[] {
+ String[] projection = new String[]{
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
UserIds.USER_ID,
@@ -142,7 +141,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
+ Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_SIGN
+ " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND "
+ "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY
- + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
+ + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID,};
String orderBy = UserIds.USER_ID + " ASC";
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
index 6bcb84f46..cbc0f4c5c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
@@ -17,17 +17,15 @@
package org.sufficientlysecure.keychain.ui;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-
import android.app.Activity;
import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -36,17 +34,34 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
-public class SelectSecretKeyLayoutFragment extends Fragment {
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+
+public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private TextView mKeyUserId;
private TextView mKeyUserIdRest;
+ private TextView mKeyMasterKeyIdHex;
+ private TextView mNoKeySelected;
private BootstrapButton mSelectKeyButton;
private Boolean mFilterCertify;
+ private Uri mReceivedUri = null;
+
private SelectSecretKeyCallback mCallback;
private static final int REQUEST_CODE_SELECT_KEY = 8882;
+ private static final int LOADER_ID = 0;
+
+ //The Projection we will retrieve, Master Key ID is for convenience sake,
+ //to avoid having to pass the Key Around
+ final String[] PROJECTION = new String[]{KeychainContract.UserIds.USER_ID
+ , KeychainContract.KeyRings.MASTER_KEY_ID};
+ final int INDEX_USER_ID = 0;
+ final int INDEX_MASTER_KEY_ID = 1;
+
public interface SelectSecretKeyCallback {
void onKeySelected(long secretKeyId);
}
@@ -59,34 +74,30 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
mFilterCertify = filterCertify;
}
- public void selectKey(long secretKeyId) {
- if (secretKeyId == Id.key.none) {
- mKeyUserId.setText(R.string.api_settings_no_key);
- mKeyUserIdRest.setText("");
- } else {
- String uid = getResources().getString(R.string.user_id_no_name);
- String uidExtra = "";
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
- getActivity(), secretKeyId);
- if (keyRing != null) {
- PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
- if (key != null) {
- String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key);
- String chunks[] = userId.split(" <", 2);
- uid = chunks[0];
- if (chunks.length > 1) {
- uidExtra = "<" + chunks[1];
- }
- }
- }
- mKeyUserId.setText(uid);
- mKeyUserIdRest.setText(uidExtra);
- }
+ public void setNoKeySelected() {
+ mNoKeySelected.setVisibility(View.VISIBLE);
+ mKeyUserId.setVisibility(View.GONE);
+ mKeyUserIdRest.setVisibility(View.GONE);
+ mKeyMasterKeyIdHex.setVisibility(View.GONE);
+ }
+
+ public void setSelectedKeyData(String userName, String email, String masterKeyHex) {
+
+ mNoKeySelected.setVisibility(View.GONE);
+
+ mKeyUserId.setText(userName);
+ mKeyUserIdRest.setText(email);
+ mKeyMasterKeyIdHex.setText(masterKeyHex);
+
+ mKeyUserId.setVisibility(View.VISIBLE);
+ mKeyUserIdRest.setVisibility(View.VISIBLE);
+ mKeyMasterKeyIdHex.setVisibility(View.VISIBLE);
+
}
public void setError(String error) {
- mKeyUserId.requestFocus();
- mKeyUserId.setError(error);
+ mNoKeySelected.requestFocus();
+ mNoKeySelected.setError(error);
}
/**
@@ -96,8 +107,10 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.select_secret_key_layout_fragment, container, false);
+ mNoKeySelected = (TextView) view.findViewById(R.id.no_key_selected);
mKeyUserId = (TextView) view.findViewById(R.id.select_secret_key_user_id);
mKeyUserIdRest = (TextView) view.findViewById(R.id.select_secret_key_user_id_rest);
+ mKeyMasterKeyIdHex = (TextView) view.findViewById(R.id.select_secret_key_master_key_hex);
mSelectKeyButton = (BootstrapButton) view
.findViewById(R.id.select_secret_key_select_key_button);
mFilterCertify = false;
@@ -111,6 +124,13 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
return view;
}
+ //For AppSettingsFragment
+ public void selectKey(long masterKeyId) {
+ Uri buildUri = KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId));
+ mReceivedUri = buildUri;
+ getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
+ }
+
private void startSelectKeyActivity() {
Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class);
intent.putExtra(SelectSecretKeyActivity.EXTRA_FILTER_CERTIFY, mFilterCertify);
@@ -118,29 +138,74 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
}
@Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ //We don't care about the Loader id
+ return new CursorLoader(getActivity(), mReceivedUri, PROJECTION, null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (data.moveToFirst()) {
+ String userName, email, masterKeyHex;
+ String userID = data.getString(INDEX_USER_ID);
+ long masterKeyID = data.getLong(INDEX_MASTER_KEY_ID);
+
+ String splitUserID[] = PgpKeyHelper.splitUserId(userID);
+
+ if (splitUserID[0] != null) {
+ userName = splitUserID[0];
+ } else {
+ userName = getActivity().getResources().getString(R.string.user_id_no_name);
+ }
+
+ if (splitUserID[1] != null) {
+ email = splitUserID[1];
+ } else {
+ email = getActivity().getResources().getString(R.string.error_user_id_no_email);
+ }
+
+ //TODO Can the cursor return invalid values for the Master Key ?
+ masterKeyHex = PgpKeyHelper.convertKeyIdToHexShort(masterKeyID);
+
+ //Set the data
+ setSelectedKeyData(userName, email, masterKeyHex);
+
+ //Give value to the callback
+ mCallback.onKeySelected(masterKeyID);
+ } else {
+ //Set The empty View
+ setNoKeySelected();
+ }
+
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ return;
+ }
+
+ // Select Secret Key Activity delivers the intent which was sent by it using interface to Select
+ // Secret Key Fragment.Intent contains the passed Uri
+ @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode & 0xFFFF) {
- case REQUEST_CODE_SELECT_KEY: {
- long secretKeyId;
- if (resultCode == Activity.RESULT_OK) {
- Bundle bundle = data.getExtras();
- secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
+ case REQUEST_CODE_SELECT_KEY: {
+ if (resultCode == Activity.RESULT_OK) {
+ mReceivedUri = data.getData();
- selectKey(secretKeyId);
+ //Must be restartLoader() or the data will not be updated on selecting a new key
+ getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
- // remove displayed errors
- mKeyUserId.setError(null);
+ mKeyUserId.setError(null);
- // give value back to callback
- mCallback.onKeySelected(secretKeyId);
+ }
+ break;
}
- break;
- }
- default:
- super.onActivityResult(requestCode, resultCode, data);
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
- break;
+ break;
}
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index 550d3047d..0e231e6a8 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -17,13 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
@@ -36,8 +29,13 @@ import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Toast;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.util.Log;
/**
* Sends the selected public key to a keyserver
@@ -59,7 +57,7 @@ public class UploadKeyActivity extends ActionBarActivity {
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
- .getKeyServers());
+ .getKeyServers());
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mKeyServerSpinner.setAdapter(adapter);
if (adapter.getCount() > 0) {
@@ -100,11 +98,11 @@ public class UploadKeyActivity extends ActionBarActivity {
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after uploading is done in ApgService
+ // Message is received after uploading is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
+ getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -113,7 +111,7 @@ public class UploadKeyActivity extends ActionBarActivity {
Toast.LENGTH_SHORT).show();
finish();
}
- };
+ }
};
// Create a new Messenger for the communication back
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 0a452bc8a..3e7e6d7ce 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
@@ -29,13 +29,13 @@ import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
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,17 +83,23 @@ 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);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_main)),
- ViewKeyMainFragment.class, mainBundle, (selectedTab == 0 ? true : false));
+ ViewKeyMainFragment.class, mainBundle, (selectedTab == 0));
Bundle certBundle = new Bundle();
certBundle.putParcelable(ViewKeyCertsFragment.ARG_DATA_URI, mDataUri);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)),
- ViewKeyCertsFragment.class, certBundle, (selectedTab == 1 ? true : false));
+ ViewKeyCertsFragment.class, certBundle, (selectedTab == 1));
}
@Override
@@ -107,7 +113,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;
@@ -118,8 +124,11 @@ public class ViewKeyActivity extends ActionBarActivity {
uploadToKeyserver(mDataUri);
return true;
case R.id.menu_key_view_export_file:
- mExportHelper.showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
- + "/pubexport.asc");
+ long masterKeyId =
+ ProviderHelper.getPublicMasterKeyId(this, Long.valueOf(mDataUri.getLastPathSegment()));
+ long[] ids = new long[]{masterKeyId};
+ mExportHelper.showExportKeysDialog(ids, Id.type.public_key,
+ Constants.Path.APP_DIR_FILE_PUB, null);
return true;
case R.id.menu_key_view_share_default_fingerprint:
shareKey(mDataUri, true);
@@ -154,7 +163,7 @@ public class ViewKeyActivity extends ActionBarActivity {
}
private void updateFromKeyserver(Uri dataUri) {
- long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, mDataUri);
+ long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, dataUri);
if (updateKeyId == 0) {
Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
@@ -173,7 +182,7 @@ public class ViewKeyActivity extends ActionBarActivity {
String content;
if (fingerprintOnly) {
byte[] fingerprintBlob = ProviderHelper.getFingerprint(this, dataUri);
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
} else {
@@ -228,24 +237,12 @@ public class ViewKeyActivity extends ActionBarActivity {
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- Bundle returnData = message.getData();
- if (returnData != null
- && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) {
- // we delete only this key, so MESSAGE_NOT_DELETED will solely contain this key
- Toast.makeText(ViewKeyActivity.this,
- getString(R.string.error_can_not_delete_contact)
- + getResources().getQuantityString(R.plurals.error_can_not_delete_info, 1),
- Toast.LENGTH_LONG).show();
- } else {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
+ setResult(RESULT_CANCELED);
+ finish();
}
};
- mExportHelper.deleteKey(dataUri, Id.type.public_key, returnHandler);
+ mExportHelper.deleteKey(dataUri, returnHandler);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java
index 59037d9ff..997ff9c7a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java
@@ -18,10 +18,6 @@
package org.sufficientlysecure.keychain.ui;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-
import android.annotation.TargetApi;
import android.net.Uri;
import android.nfc.NdefMessage;
@@ -35,6 +31,9 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMessageCallback,
@@ -66,7 +65,7 @@ public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMess
// get public keyring as byte array
long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
mSharedKeyringBytes = ProviderHelper.getKeyRingsAsByteArray(this, dataUri,
- new long[] { masterKeyId });
+ new long[]{masterKeyId});
// Register callback to set NDEF message
mNfcAdapter.setNdefPushMessageCallback(this, this);
@@ -109,10 +108,10 @@ public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMess
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case NFC_SENT:
- Toast.makeText(getApplicationContext(), R.string.nfc_successfull, Toast.LENGTH_LONG)
- .show();
- break;
+ case NFC_SENT:
+ Toast.makeText(getApplicationContext(), R.string.nfc_successfull, Toast.LENGTH_LONG)
+ .show();
+ break;
}
}
};
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
index 36d3e6ace..02c04c11d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
@@ -24,9 +24,7 @@ import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
@@ -89,4 +87,4 @@ public class ViewKeyCertsFragment extends Fragment {
startActivity(signIntent);
}
-} \ No newline at end of file
+}
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 495764eaf..691be5fa9 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
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@@ -36,6 +37,7 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -46,8 +48,8 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
-public class ViewKeyMainFragment extends Fragment implements
- LoaderManager.LoaderCallbacks<Cursor>{
+public class ViewKeyMainFragment extends Fragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri";
@@ -59,7 +61,10 @@ 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 BootstrapButton mActionCertify;
private ListView mUserIds;
private ListView mKeys;
@@ -85,9 +90,12 @@ 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);
+ mActionCertify = (BootstrapButton) view.findViewById(R.id.action_certify);
return view;
}
@@ -116,6 +124,53 @@ 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);
+
+ // certify button
+ // TODO this button MIGHT be useful if the user wants to
+ // certify a private key with another...
+ // mActionCertify.setVisibility(View.GONE);
+
+ // 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));
+
+ // certify button
+ mActionCertify.setVisibility(View.VISIBLE);
+ // edit button
+ mActionEdit.setVisibility(View.GONE);
+ }
+
+ // TODO see todo note above, doing this here for now
+ mActionCertify.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ certifyKey(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
+ Long.toString(masterKeyId)
+ ));
+ }
+ });
+
+ }
+
mActionEncrypt.setOnClickListener(new View.OnClickListener() {
@Override
@@ -137,21 +192,29 @@ public class ViewKeyMainFragment extends Fragment implements
getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this);
}
- static final String[] KEYRING_PROJECTION = new String[]{KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID,
- KeychainContract.UserIds.USER_ID};
+ static final String[] KEYRING_PROJECTION =
+ new String[]{KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.UserIds.USER_ID};
static final int KEYRING_INDEX_ID = 0;
static final int KEYRING_INDEX_MASTER_KEY_ID = 1;
static final int KEYRING_INDEX_USER_ID = 2;
- static final String[] USER_IDS_PROJECTION = new String[]{KeychainContract.UserIds._ID, KeychainContract.UserIds.USER_ID,
- KeychainContract.UserIds.RANK,};
- // not the main user id
- static final String USER_IDS_SELECTION = KeychainContract.UserIds.RANK + " > 0 ";
- static final String USER_IDS_SORT_ORDER = KeychainContract.UserIds.USER_ID + " COLLATE LOCALIZED ASC";
-
- static final String[] KEYS_PROJECTION = new String[]{KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID,
- KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM, KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY, KeychainContract.Keys.CAN_SIGN,
- KeychainContract.Keys.CAN_ENCRYPT, KeychainContract.Keys.CREATION, KeychainContract.Keys.EXPIRY, KeychainContract.Keys.FINGERPRINT};
+ static final String[] USER_IDS_PROJECTION =
+ new String[]{
+ KeychainContract.UserIds._ID,
+ KeychainContract.UserIds.USER_ID,
+ KeychainContract.UserIds.RANK,
+ };
+ static final String USER_IDS_SORT_ORDER =
+ KeychainContract.UserIds.RANK + " COLLATE LOCALIZED ASC";
+
+ static final String[] KEYS_PROJECTION =
+ new String[]{KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID,
+ KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM,
+ KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY,
+ KeychainContract.Keys.CAN_SIGN, KeychainContract.Keys.CAN_ENCRYPT,
+ KeychainContract.Keys.IS_REVOKED, KeychainContract.Keys.CREATION,
+ KeychainContract.Keys.EXPIRY, KeychainContract.Keys.FINGERPRINT};
static final String KEYS_SORT_ORDER = KeychainContract.Keys.RANK + " ASC";
static final int KEYS_INDEX_ID = 0;
static final int KEYS_INDEX_KEY_ID = 1;
@@ -161,9 +224,10 @@ public class ViewKeyMainFragment extends Fragment implements
static final int KEYS_INDEX_CAN_CERTIFY = 5;
static final int KEYS_INDEX_CAN_SIGN = 6;
static final int KEYS_INDEX_CAN_ENCRYPT = 7;
- static final int KEYS_INDEX_CREATION = 8;
- static final int KEYS_INDEX_EXPIRY = 9;
- static final int KEYS_INDEX_FINGERPRINT = 10;
+ static final int KEYS_INDEX_IS_REVOKED = 8;
+ static final int KEYS_INDEX_CREATION = 9;
+ static final int KEYS_INDEX_EXPIRY = 10;
+ static final int KEYS_INDEX_FINGERPRINT = 11;
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
@@ -179,7 +243,7 @@ public class ViewKeyMainFragment extends Fragment implements
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, USER_IDS_PROJECTION, USER_IDS_SELECTION, null,
+ return new CursorLoader(getActivity(), baseUri, USER_IDS_PROJECTION, null, null,
USER_IDS_SORT_ORDER);
}
case LOADER_ID_KEYS: {
@@ -224,8 +288,7 @@ public class ViewKeyMainFragment extends Fragment implements
if (data.moveToFirst()) {
// get key id from MASTER_KEY_ID
long keyId = data.getLong(KEYS_INDEX_KEY_ID);
-
- String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId);
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
mKeyId.setText(keyIdStr);
// get creation date from CREATION
@@ -234,8 +297,9 @@ public class ViewKeyMainFragment extends Fragment implements
} else {
Date creationDate = new Date(data.getLong(KEYS_INDEX_CREATION) * 1000);
- mCreation.setText(DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
- creationDate));
+ mCreation.setText(
+ DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
+ creationDate));
}
// get expiry date from EXPIRY
@@ -244,8 +308,9 @@ public class ViewKeyMainFragment extends Fragment implements
} else {
Date expiryDate = new Date(data.getLong(KEYS_INDEX_EXPIRY) * 1000);
- mExpiry.setText(DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
- expiryDate));
+ mExpiry.setText(
+ DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
+ expiryDate));
}
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
@@ -257,10 +322,22 @@ public class ViewKeyMainFragment extends Fragment implements
// FALLBACK for old database entries
fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri);
}
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);
- fingerprint = fingerprint.replace(" ", "\n");
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
+
+ mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
+ }
- mFingerprint.setText(fingerprint);
+ // hide encrypt button if no encryption key is available
+ boolean canEncrypt = false;
+ data.moveToFirst();
+ do {
+ if (data.getInt(KEYS_INDEX_CAN_ENCRYPT) == 1) {
+ canEncrypt = true;
+ break;
+ }
+ } while (data.moveToNext());
+ if (!canEncrypt) {
+ mActionEncrypt.setVisibility(View.GONE);
}
mKeysAdapter.swapCursor(data);
@@ -309,5 +386,4 @@ public class ViewKeyMainFragment extends Fragment implements
startActivity(signIntent);
}
-
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java
index 2ac19c1d9..5f2aec4fe 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java
@@ -22,24 +22,25 @@ package org.sufficientlysecure.keychain.ui.adapter;
* You can pass the result and an exception in it if an error occurred.
* Concept found at:
* https://stackoverflow.com/questions/19593577/how-to-handle-errors-in-custom-asynctaskloader
+ *
* @param <T> - Typ of the result which is wrapped
*/
-public class AsyncTaskResultWrapper <T>{
+public class AsyncTaskResultWrapper<T> {
- private final T result;
- private final Exception error;
+ private final T mResult;
+ private final Exception mError;
- public AsyncTaskResultWrapper(T result, Exception error){
- this.result = result;
- this.error = error;
+ public AsyncTaskResultWrapper(T result, Exception error) {
+ this.mResult = result;
+ this.mError = error;
}
public T getResult() {
- return result;
+ return mResult;
}
public Exception getError() {
- return error;
+ return mError;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java
new file mode 100644
index 000000000..a3ed08a4c
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 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.adapter;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.support.v4.widget.CursorAdapter;
+import android.text.Spannable;
+import android.text.style.ForegroundColorSpan;
+import org.sufficientlysecure.keychain.R;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class HighlightQueryCursorAdapter extends CursorAdapter {
+
+ private String mCurQuery;
+
+ public HighlightQueryCursorAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+ mCurQuery = null;
+ }
+
+ public void setSearchQuery(String searchQuery) {
+ mCurQuery = searchQuery;
+ }
+
+ public String getSearchQuery() {
+ return mCurQuery;
+ }
+
+ protected Spannable highlightSearchQuery(String text) {
+ Spannable highlight = Spannable.Factory.getInstance().newSpannable(text);
+
+ if (mCurQuery != null) {
+ Pattern pattern = Pattern.compile("(?i)" + mCurQuery);
+ Matcher matcher = pattern.matcher(text);
+ if (matcher.find()) {
+ highlight.setSpan(
+ new ForegroundColorSpan(mContext.getResources().getColor(R.color.emphasis)),
+ matcher.start(),
+ matcher.end(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ return highlight;
+ } else {
+ return highlight;
+ }
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index 52186b662..7d3166af9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -17,12 +17,6 @@
package org.sufficientlysecure.keychain.ui.adapter;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
@@ -37,11 +31,27 @@ import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
protected LayoutInflater mInflater;
protected Activity mActivity;
- protected List<ImportKeysListEntry> data;
+ protected List<ImportKeysListEntry> mData;
+
+ static class ViewHolder {
+ private TextView mainUserId;
+ private TextView mainUserIdRest;
+ private TextView keyId;
+ private TextView fingerprint;
+ private TextView algorithm;
+ private TextView status;
+ }
public ImportKeysAdapter(Activity activity) {
super(activity, -1);
@@ -53,7 +63,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
public void setData(List<ImportKeysListEntry> data) {
clear();
if (data != null) {
- this.data = data;
+ this.mData = data;
// add data to extended ArrayAdapter
if (Build.VERSION.SDK_INT >= 11) {
@@ -67,14 +77,15 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
}
public List<ImportKeysListEntry> getData() {
- return data;
+ return mData;
}
public ArrayList<ImportKeysListEntry> getSelectedData() {
ArrayList<ImportKeysListEntry> selectedData = new ArrayList<ImportKeysListEntry>();
- for (ImportKeysListEntry entry : data) {
- if (entry.isSelected())
+ for (ImportKeysListEntry entry : mData) {
+ if (entry.isSelected()) {
selectedData.add(entry);
+ }
}
return selectedData;
}
@@ -85,17 +96,21 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
}
public View getView(int position, View convertView, ViewGroup parent) {
- ImportKeysListEntry entry = data.get(position);
-
- View view = mInflater.inflate(R.layout.import_keys_list_entry, null);
-
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- TextView fingerprint = (TextView) view.findViewById(R.id.fingerprint);
- TextView algorithm = (TextView) view.findViewById(R.id.algorithm);
- TextView status = (TextView) view.findViewById(R.id.status);
-
+ ImportKeysListEntry entry = mData.get(position);
+ ViewHolder holder;
+ if (convertView == null) {
+ holder = new ViewHolder();
+ convertView = mInflater.inflate(R.layout.import_keys_list_entry, null);
+ holder.mainUserId = (TextView) convertView.findViewById(R.id.mainUserId);
+ holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest);
+ holder.keyId = (TextView) convertView.findViewById(R.id.keyId);
+ holder.fingerprint = (TextView) convertView.findViewById(R.id.fingerprint);
+ holder.algorithm = (TextView) convertView.findViewById(R.id.algorithm);
+ holder.status = (TextView) convertView.findViewById(R.id.status);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
// main user id
String userId = entry.userIds.get(0);
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
@@ -105,39 +120,40 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
// show red user id if it is a secret key
if (entry.secretKey) {
userIdSplit[0] = mActivity.getString(R.string.secret_key) + " " + userIdSplit[0];
- mainUserId.setTextColor(Color.RED);
+ holder.mainUserId.setTextColor(Color.RED);
}
- mainUserId.setText(userIdSplit[0]);
+ holder.mainUserId.setText(userIdSplit[0]);
} else {
- mainUserId.setText(R.string.user_id_no_name);
+ holder.mainUserId.setText(R.string.user_id_no_name);
}
// email
if (userIdSplit[1] != null) {
- mainUserIdRest.setText(userIdSplit[1]);
- mainUserIdRest.setVisibility(View.VISIBLE);
+ holder.mainUserIdRest.setText(userIdSplit[1]);
+ holder.mainUserIdRest.setVisibility(View.VISIBLE);
} else {
- mainUserIdRest.setVisibility(View.GONE);
+ holder.mainUserIdRest.setVisibility(View.GONE);
}
- keyId.setText(entry.hexKeyId);
+ holder.keyId.setText(entry.keyIdHex);
- if (entry.fingerPrint != null) {
- fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint);
- fingerprint.setVisibility(View.VISIBLE);
+ if (entry.fingerPrintHex != null) {
+ holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerPrintHex));
+ holder.fingerprint.setVisibility(View.VISIBLE);
} else {
- fingerprint.setVisibility(View.GONE);
+ holder.fingerprint.setVisibility(View.GONE);
}
- algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
+ holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
if (entry.revoked) {
- status.setText(R.string.revoked);
+ holder.status.setText(R.string.revoked);
} else {
- status.setVisibility(View.GONE);
+ holder.status.setVisibility(View.GONE);
}
- LinearLayout ll = (LinearLayout) view.findViewById(R.id.list);
+ LinearLayout ll = (LinearLayout) convertView.findViewById(R.id.list);
+ ll.removeAllViews();
if (entry.userIds.size() == 1) {
ll.setVisibility(View.GONE);
} else {
@@ -162,10 +178,10 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
}
}
- CheckBox cBox = (CheckBox) view.findViewById(R.id.selected);
+ CheckBox cBox = (CheckBox) convertView.findViewById(R.id.selected);
cBox.setChecked(entry.isSelected());
- return view;
+ return convertView;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
index 4a7a9c93a..9b20effc2 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
@@ -19,11 +19,7 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.os.Parcel;
import android.os.Parcelable;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Date;
+import android.util.SparseArray;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
@@ -33,35 +29,40 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+
public class ImportKeysListEntry implements Serializable, Parcelable {
private static final long serialVersionUID = -7797972103284992662L;
- public ArrayList<String> userIds;
+ public ArrayList<String> userIds;
public long keyId;
+ public String keyIdHex;
public boolean revoked;
public Date date; // TODO: not displayed
- public String fingerPrint;
- public String hexKeyId;
+ public String fingerPrintHex;
public int bitStrength;
public String algorithm;
public boolean secretKey;
- private boolean selected;
+ private boolean mSelected;
- private byte[] bytes = new byte[]{};
+ private byte[] mBytes = new byte[]{};
public ImportKeysListEntry(ImportKeysListEntry b) {
this.userIds = b.userIds;
this.keyId = b.keyId;
this.revoked = b.revoked;
this.date = b.date;
- this.fingerPrint = b.fingerPrint;
- this.hexKeyId = b.hexKeyId;
+ this.fingerPrintHex = b.fingerPrintHex;
+ this.keyIdHex = b.keyIdHex;
this.bitStrength = b.bitStrength;
this.algorithm = b.algorithm;
this.secretKey = b.secretKey;
- this.selected = b.selected;
- this.bytes = b.bytes;
+ this.mSelected = b.mSelected;
+ this.mBytes = b.mBytes;
}
public int describeContents() {
@@ -74,14 +75,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
dest.writeLong(keyId);
dest.writeByte((byte) (revoked ? 1 : 0));
dest.writeSerializable(date);
- dest.writeString(fingerPrint);
- dest.writeString(hexKeyId);
+ dest.writeString(fingerPrintHex);
+ dest.writeString(keyIdHex);
dest.writeInt(bitStrength);
dest.writeString(algorithm);
dest.writeByte((byte) (secretKey ? 1 : 0));
- dest.writeByte((byte) (selected ? 1 : 0));
- dest.writeInt(bytes.length);
- dest.writeByteArray(bytes);
+ dest.writeByte((byte) (mSelected ? 1 : 0));
+ dest.writeInt(mBytes.length);
+ dest.writeByteArray(mBytes);
}
public static final Creator<ImportKeysListEntry> CREATOR = new Creator<ImportKeysListEntry>() {
@@ -92,14 +93,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.keyId = source.readLong();
vr.revoked = source.readByte() == 1;
vr.date = (Date) source.readSerializable();
- vr.fingerPrint = source.readString();
- vr.hexKeyId = source.readString();
+ vr.fingerPrintHex = source.readString();
+ vr.keyIdHex = source.readString();
vr.bitStrength = source.readInt();
vr.algorithm = source.readString();
vr.secretKey = source.readByte() == 1;
- vr.selected = source.readByte() == 1;
- vr.bytes = new byte[source.readInt()];
- source.readByteArray(vr.bytes);
+ vr.mSelected = source.readByte() == 1;
+ vr.mBytes = new byte[source.readInt()];
+ source.readByteArray(vr.mBytes);
return vr;
}
@@ -109,34 +110,105 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
}
};
- public long getKeyId() {
- return keyId;
+ public String getKeyIdHex() {
+ return keyIdHex;
}
public byte[] getBytes() {
- return bytes;
+ return mBytes;
}
public void setBytes(byte[] bytes) {
- this.bytes = bytes;
+ this.mBytes = bytes;
+ }
+
+ public boolean isSelected() {
+ return mSelected;
+ }
+
+ public void setSelected(boolean selected) {
+ this.mSelected = selected;
+ }
+
+ public long getKeyId() {
+ return keyId;
+ }
+
+ public void setKeyId(long keyId) {
+ this.keyId = keyId;
+ }
+
+ public void setKeyIdHex(String keyIdHex) {
+ this.keyIdHex = keyIdHex;
+ }
+
+ public boolean isRevoked() {
+ return revoked;
+ }
+
+ public void setRevoked(boolean revoked) {
+ this.revoked = revoked;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getFingerPrintHex() {
+ return fingerPrintHex;
+ }
+
+ public void setFingerPrintHex(String fingerPrintHex) {
+ this.fingerPrintHex = fingerPrintHex;
+ }
+
+ public int getBitStrength() {
+ return bitStrength;
+ }
+
+ public void setBitStrength(int bitStrength) {
+ this.bitStrength = bitStrength;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ public boolean isSecretKey() {
+ return secretKey;
+ }
+
+ public void setSecretKey(boolean secretKey) {
+ this.secretKey = secretKey;
+ }
+
+ public ArrayList<String> getUserIds() {
+ return userIds;
+ }
+
+ public void setUserIds(ArrayList<String> userIds) {
+ this.userIds = userIds;
}
/**
* Constructor for later querying from keyserver
*/
public ImportKeysListEntry() {
+ // keys from keyserver are always public keys
secretKey = false;
+ // do not select by default
+ mSelected = false;
userIds = new ArrayList<String>();
}
- public boolean isSelected() {
- return selected;
- }
-
- public void setSelected(boolean selected) {
- this.selected = selected;
- }
-
/**
* Constructor based on key object, used for import from NFC, QR Codes, files
*/
@@ -144,13 +216,13 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
public ImportKeysListEntry(PGPKeyRing pgpKeyRing) {
// save actual key object into entry, used to import it later
try {
- this.bytes = pgpKeyRing.getEncoded();
+ this.mBytes = pgpKeyRing.getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "IOException on pgpKeyRing.getEncoded()", e);
}
// selected is default
- this.selected = true;
+ this.mSelected = true;
if (pgpKeyRing instanceof PGPSecretKeyRing) {
secretKey = true;
@@ -162,27 +234,39 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
for (String userId : new IterableIterator<String>(pgpKeyRing.getPublicKey().getUserIDs())) {
userIds.add(userId);
}
+
this.keyId = pgpKeyRing.getPublicKey().getKeyID();
+ this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
this.revoked = pgpKeyRing.getPublicKey().isRevoked();
- this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
- .getFingerprint(), true);
- this.hexKeyId = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId);
+ this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
+ .getFingerprint());
this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength();
- int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
- if (algorithm == PGPPublicKey.RSA_ENCRYPT || algorithm == PGPPublicKey.RSA_GENERAL
- || algorithm == PGPPublicKey.RSA_SIGN) {
- this.algorithm = "RSA";
- } else if (algorithm == PGPPublicKey.DSA) {
- this.algorithm = "DSA";
- } else if (algorithm == PGPPublicKey.ELGAMAL_ENCRYPT
- || algorithm == PGPPublicKey.ELGAMAL_GENERAL) {
- this.algorithm = "ElGamal";
- } else if (algorithm == PGPPublicKey.EC || algorithm == PGPPublicKey.ECDSA) {
- this.algorithm = "ECC";
- } else {
- // TODO: with resources
- this.algorithm = "unknown";
- }
+ final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
+ this.algorithm = getAlgorithmFromId(algorithm);
+ }
+
+ /**
+ * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
+ */
+ private final static SparseArray<String> ALGORITHM_IDS = new SparseArray<String>() {{
+ put(-1, "unknown"); // TODO: with resources
+ put(0, "unencrypted");
+ put(PGPPublicKey.RSA_GENERAL, "RSA");
+ put(PGPPublicKey.RSA_ENCRYPT, "RSA");
+ put(PGPPublicKey.RSA_SIGN, "RSA");
+ put(PGPPublicKey.ELGAMAL_ENCRYPT, "ElGamal");
+ put(PGPPublicKey.ELGAMAL_GENERAL, "ElGamal");
+ put(PGPPublicKey.DSA, "DSA");
+ put(PGPPublicKey.EC, "ECC");
+ put(PGPPublicKey.ECDSA, "ECC");
+ put(PGPPublicKey.ECDH, "ECC");
+ }};
+
+ /**
+ * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
+ */
+ public static String getAlgorithmFromId(int algorithmId) {
+ return (ALGORITHM_IDS.get(algorithmId) != null ? ALGORITHM_IDS.get(algorithmId) : ALGORITHM_IDS.get(-1));
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
index 76649b27b..c9983213c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -17,11 +17,8 @@
package org.sufficientlysecure.keychain.ui.adapter;
-import java.io.BufferedInputStream;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
+import android.content.Context;
+import android.support.v4.content.AsyncTaskLoader;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPUtil;
@@ -30,16 +27,35 @@ import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
-import android.content.Context;
-import android.support.v4.content.AsyncTaskLoader;
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+public class ImportKeysListLoader
+ extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
+
+ public static class FileHasNoContent extends Exception {
+
+ }
+
+ public static class NonPgpPart extends Exception {
+ private int mCount;
+
+ public NonPgpPart(int count) {
+ this.mCount = count;
+ }
+
+ public int getCount() {
+ return mCount;
+ }
+ }
-public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
Context mContext;
InputData mInputData;
- ArrayList<ImportKeysListEntry> data = new ArrayList<ImportKeysListEntry>();
- AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> entryListWrapper;
+ ArrayList<ImportKeysListEntry> mData = new ArrayList<ImportKeysListEntry>();
+ AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
public ImportKeysListLoader(Context context, InputData inputData) {
super(context);
@@ -50,16 +66,16 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper
@Override
public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
- entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(data, null);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, null);
if (mInputData == null) {
Log.e(Constants.TAG, "Input data is null!");
- return entryListWrapper;
+ return mEntryListWrapper;
}
generateListOfKeyrings(mInputData);
- return entryListWrapper;
+ return mEntryListWrapper;
}
@Override
@@ -87,11 +103,15 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper
/**
* Reads all PGPKeyRing objects from input
- *
- * @param keyringBytes
+ *
+ * @param inputData
* @return
*/
private void generateListOfKeyrings(InputData inputData) {
+
+ boolean isEmpty = true;
+ int nonPgpCounter = 0;
+
PositionAwareInputStream progressIn = new PositionAwareInputStream(
inputData.getInputStream());
@@ -103,6 +123,7 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper
// read all available blocks... (asc files can contain many blocks with BEGIN END)
while (bufferedInput.available() > 0) {
+ isEmpty = false;
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
@@ -116,17 +137,31 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper
addToData(newKeyring);
} else {
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
+ nonPgpCounter++;
}
}
}
} catch (Exception e) {
Log.e(Constants.TAG, "Exception on parsing key file!", e);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
+ nonPgpCounter = 0;
+ }
+
+ if (isEmpty) {
+ Log.e(Constants.TAG, "File has no content!", new FileHasNoContent());
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
+ (mData, new FileHasNoContent());
+ }
+
+ if (nonPgpCounter > 0) {
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
+ (mData, new NonPgpPart(nonPgpCounter));
}
}
private void addToData(PGPKeyRing keyring) {
ImportKeysListEntry item = new ImportKeysListEntry(keyring);
- data.add(item);
+ mData.add(item);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java
index 3a3b6e58b..a4dd06e40 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java
@@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.HkpKeyServer;
import org.sufficientlysecure.keychain.util.KeyServer;
@@ -27,14 +26,15 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
-public class ImportKeysListServerLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
+public class ImportKeysListServerLoader
+ extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
Context mContext;
String mServerQuery;
String mKeyServer;
- private ArrayList<ImportKeysListEntry> entryList = new ArrayList<ImportKeysListEntry>();
- private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> entryListWrapper;
+ private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<ImportKeysListEntry>();
+ private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
public ImportKeysListServerLoader(Context context, String serverQuery, String keyServer) {
super(context);
@@ -46,16 +46,16 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<AsyncTaskResultW
@Override
public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
- entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, null);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
if (mServerQuery == null) {
Log.e(Constants.TAG, "mServerQuery is null!");
- return entryListWrapper;
+ return mEntryListWrapper;
}
queryServer(mServerQuery, mKeyServer);
- return entryListWrapper;
+ return mEntryListWrapper;
}
@Override
@@ -89,18 +89,19 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<AsyncTaskResultW
try {
ArrayList<ImportKeysListEntry> searchResult = server.search(query);
+ mEntryList.clear();
// add result to data
- entryList.addAll(searchResult);
- entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, null);
+ mEntryList.addAll(searchResult);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
} catch (KeyServer.InsufficientQuery e) {
Log.e(Constants.TAG, "InsufficientQuery", e);
- entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
} catch (KeyServer.QueryException e) {
Log.e(Constants.TAG, "QueryException", e);
- entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
} catch (KeyServer.TooManyResponses e) {
Log.e(Constants.TAG, "TooManyResponses", e);
- entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
}
}
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 ac505adfb..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ /dev/null
@@ -1,233 +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 java.util.Set;
-
-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.support.v4.widget.CursorAdapter;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-/**
- * Implements StickyListHeadersAdapter from library
- */
-public class KeyListPublicAdapter extends CursorAdapter 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(userIdSplit[0]);
- } else {
- mainUserId.setText(R.string.user_id_no_name);
- }
- if (userIdSplit[1] != null) {
- mainUserIdRest.setText(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 boolean isPositionChecked(int position) {
- Boolean result = mSelection.get(position);
- return result == null ? false : result;
- }
-
- public Set<Integer> getCurrentCheckedPosition() {
- return mSelection.keySet();
- }
-
- public void removeSelection(int position) {
- mSelection.remove(position);
- notifyDataSetChanged();
- }
-
- public void clearSelection() {
- mSelection.clear();
- notifyDataSetChanged();
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // let the adapter handle setting up the row views
- View v = super.getView(position, convertView, parent);
-
- /**
- * Change color for multi-selection
- */
- // default color
- v.setBackgroundColor(Color.TRANSPARENT);
- if (mSelection.get(position) != null && mSelection.get(position).booleanValue()) {
- // this is a selected position, change color!
- v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
- }
- 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 11d1e8c17..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
+++ /dev/null
@@ -1,138 +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 java.util.Set;
-
-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 boolean isPositionChecked(int position) {
- Boolean result = mSelection.get(position);
- return result == null ? false : result;
- }
-
- public Set<Integer> getCurrentCheckedPosition() {
- return mSelection.keySet();
- }
-
- public void removeSelection(int position) {
- mSelection.remove(position);
- notifyDataSetChanged();
- }
-
- public void clearSelection() {
- mSelection.clear();
- notifyDataSetChanged();
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // let the adapter handle setting up the row views
- View v = super.getView(position, convertView, parent);
-
- /**
- * Change color for multi-selection
- */
- // default color
- v.setBackgroundColor(Color.TRANSPARENT);
- if (mSelection.get(position) != null) {
- // this is a selected position, change color!
- v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
- }
- return v;
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
index 78f7b1f7e..c997599bd 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
@@ -17,15 +17,11 @@
package org.sufficientlysecure.keychain.ui.adapter;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
import android.content.Context;
import android.widget.ArrayAdapter;
+import java.util.*;
+
public class KeyValueSpinnerAdapter extends ArrayAdapter<String> {
private final HashMap<Integer, String> mData;
private final int[] mKeys;
@@ -98,4 +94,4 @@ public class KeyValueSpinnerAdapter extends ArrayAdapter<String> {
}
return -1;
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
index d44dd5890..fbbb9caa4 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
@@ -17,23 +17,22 @@
package org.sufficientlysecure.keychain.ui.adapter;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-
import android.content.Context;
import android.database.Cursor;
-import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-public class SelectKeyCursorAdapter extends CursorAdapter {
+
+public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter {
protected int mKeyType;
@@ -45,17 +44,16 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
private int mIndexProjectionValid;
private int mIndexProjectionAvailable;
- public final static String PROJECTION_ROW_AVAILABLE = "available";
- public final static String PROJECTION_ROW_VALID = "valid";
+ public static final String PROJECTION_ROW_AVAILABLE = "available";
+ public static final String PROJECTION_ROW_VALID = "valid";
public SelectKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView,
- int keyType) {
+ int keyType) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
mListView = listView;
mKeyType = keyType;
-
initIndex(c);
}
@@ -69,7 +67,7 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
/**
* 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) {
@@ -104,12 +102,12 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
if (userIdSplit[0] != null) {
- mainUserId.setText(userIdSplit[0]);
+ mainUserId.setText(highlightSearchQuery(userIdSplit[0]));
} else {
mainUserId.setText(R.string.user_id_no_name);
}
if (userIdSplit[1] != null) {
- mainUserIdRest.setText(userIdSplit[1]);
+ mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1]));
} else {
mainUserIdRest.setText("");
}
@@ -117,7 +115,7 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
// TODO: needed to key id to no?
keyId.setText(R.string.no_key);
long masterKeyId = cursor.getLong(mIndexMasterKeyId);
- keyId.setText(PgpKeyHelper.convertKeyIdToHex(masterKeyId));
+ keyId.setText(PgpKeyHelper.convertKeyIdToHexShort(masterKeyId));
// TODO: needed to set unknown_status?
status.setText(R.string.unknown_status);
@@ -164,5 +162,4 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.select_key_item, null);
}
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
index 924a70897..f435d46ef 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
@@ -1,3 +1,20 @@
+/*
+ * 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.adapter;
import android.content.Context;
@@ -19,12 +36,12 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
- private final Class<?> clss;
- private final Bundle args;
+ private final Class<?> mClss;
+ private final Bundle mArgs;
- TabInfo(Class<?> _class, Bundle _args) {
- clss = _class;
- args = _args;
+ TabInfo(Class<?> mClss, Bundle mArgs) {
+ this.mClss = mClss;
+ this.mArgs = mArgs;
}
}
@@ -54,7 +71,7 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.
@Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
- return Fragment.instantiate(mContext, info.clss.getName(), info.args);
+ return Fragment.instantiate(mContext, info.mClss.getName(), info.mArgs);
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
@@ -81,4 +98,4 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
index 54c7eb60e..9d60c1530 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
@@ -17,18 +17,23 @@
package org.sufficientlysecure.keychain.ui.adapter;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-
import android.content.Context;
+import android.content.res.ColorStateList;
import android.database.Cursor;
+import android.graphics.Color;
import android.support.v4.widget.CursorAdapter;
+import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+
+import java.util.Date;
public class ViewKeyKeysAdapter extends CursorAdapter {
private LayoutInflater mInflater;
@@ -40,6 +45,10 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
private int mIndexCanCertify;
private int mIndexCanEncrypt;
private int mIndexCanSign;
+ private int mIndexRevokedKey;
+ private int mIndexExpiry;
+
+ private ColorStateList mDefaultTextColor;
public ViewKeyKeysAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
@@ -59,7 +68,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
/**
* 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) {
@@ -71,6 +80,8 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
mIndexCanCertify = cursor.getColumnIndexOrThrow(Keys.CAN_CERTIFY);
mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT);
mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN);
+ mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED);
+ mIndexExpiry = cursor.getColumnIndexOrThrow(Keys.EXPIRY);
}
}
@@ -78,17 +89,18 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
public void bindView(View view, Context context, Cursor cursor) {
TextView keyId = (TextView) view.findViewById(R.id.keyId);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
+ TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
+ ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
- String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId));
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(mIndexKeyId));
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm),
cursor.getInt(mIndexKeySize));
keyId.setText(keyIdStr);
-
keyDetails.setText("(" + algorithmStr + ")");
if (cursor.getInt(mIndexIsMasterKey) != 1) {
@@ -114,11 +126,52 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
} else {
signIcon.setVisibility(View.VISIBLE);
}
+
+ boolean valid = true;
+ if (cursor.getInt(mIndexRevokedKey) > 0) {
+ revokedKeyIcon.setVisibility(View.VISIBLE);
+
+ valid = false;
+ } else {
+ keyId.setTextColor(mDefaultTextColor);
+ keyDetails.setTextColor(mDefaultTextColor);
+ keyExpiry.setTextColor(mDefaultTextColor);
+
+ revokedKeyIcon.setVisibility(View.GONE);
+ }
+
+ if (!cursor.isNull(mIndexExpiry)) {
+ Date expiryDate = new Date(cursor.getLong(mIndexExpiry) * 1000);
+
+ valid = valid && expiryDate.after(new Date());
+ keyExpiry.setText("(" +
+ context.getString(R.string.label_expiry) + ": " +
+ DateFormat.getDateFormat(context).format(expiryDate) + ")");
+
+ keyExpiry.setVisibility(View.VISIBLE);
+ }
+ else {
+ keyExpiry.setVisibility(View.GONE);
+ }
+ // if key is expired or revoked, strike through text
+ if (!valid) {
+ keyId.setText(OtherHelper.strikeOutText(keyId.getText()));
+ keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText()));
+ keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText()));
+ }
+ keyId.setEnabled(valid);
+ keyDetails.setEnabled(valid);
+ keyExpiry.setEnabled(valid);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.view_key_keys_item, null);
+ View view = mInflater.inflate(R.layout.view_key_keys_item, null);
+ if (mDefaultTextColor == null) {
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ mDefaultTextColor = keyId.getTextColors();
+ }
+ return view;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
index cf8699417..61572b9ce 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
@@ -17,33 +17,54 @@
package org.sufficientlysecure.keychain.ui.adapter;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
import android.widget.TextView;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+
+import java.util.ArrayList;
public class ViewKeyUserIdsAdapter extends CursorAdapter {
private LayoutInflater mInflater;
- private int mIndexUserId;
+ private int mIndexUserId, mIndexRank;
- public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags) {
+ final private ArrayList<Boolean> mCheckStates;
+
+ public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
+ mCheckStates = showCheckBoxes ? new ArrayList<Boolean>() : null;
+
initIndex(c);
}
+ public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags) {
+ this(context, c, flags, false);
+ }
@Override
public Cursor swapCursor(Cursor newCursor) {
initIndex(newCursor);
+ if(mCheckStates != null) {
+ mCheckStates.clear();
+ if(newCursor != null) {
+ int count = newCursor.getCount();
+ mCheckStates.ensureCapacity(count);
+ // initialize to true (use case knowledge: we usually want to sign all uids)
+ for(int i = 0; i < count; i++)
+ mCheckStates.add(true);
+ }
+ }
return super.swapCursor(newCursor);
}
@@ -51,26 +72,73 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
/**
* 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);
+ mIndexRank = cursor.getColumnIndexOrThrow(UserIds.RANK);
}
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
- String userIdStr = cursor.getString(mIndexUserId);
- TextView userId = (TextView) view.findViewById(R.id.userId);
- userId.setText(userIdStr);
+ TextView vRank = (TextView) view.findViewById(R.id.rank);
+ TextView vUserId = (TextView) view.findViewById(R.id.userId);
+ TextView vAddress = (TextView) view.findViewById(R.id.address);
+
+ vRank.setText(Integer.toString(cursor.getInt(mIndexRank)));
+
+ String[] userId = PgpKeyHelper.splitUserId(cursor.getString(mIndexUserId));
+ if (userId[0] != null) {
+ vUserId.setText(userId[0]);
+ } else {
+ vUserId.setText(R.string.user_id_no_name);
+ }
+ vAddress.setText(userId[1]);
+
+ // don't care further if checkboxes aren't shown
+ if(mCheckStates == null)
+ return;
+
+ final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.checkBox);
+ final int position = cursor.getPosition();
+ vCheckBox.setClickable(false);
+ vCheckBox.setChecked(mCheckStates.get(position));
+ vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ mCheckStates.set(position, b);
+ }
+ });
+ view.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ vCheckBox.toggle();
+ }
+ });
+
+ }
+
+ public ArrayList<String> getSelectedUserIds() {
+ ArrayList<String> result = new ArrayList<String>();
+ for(int i = 0; i < mCheckStates.size(); i++) {
+ if(mCheckStates.get(i)) {
+ mCursor.moveToPosition(i);
+ result.add(mCursor.getString(mIndexUserId));
+ }
+ }
+ return result;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.view_key_userids_item, null);
+ View view = mInflater.inflate(R.layout.view_key_userids_item, null);
+ // only need to do this once ever, since mShowCheckBoxes is final
+ view.findViewById(R.id.checkBox).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE);
+ return view;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java
index 2b8cba857..20b70658c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java
@@ -23,7 +23,6 @@ import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
-
import org.sufficientlysecure.keychain.R;
public class BadImportKeyDialogFragment extends DialogFragment {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
index 98b677511..ffb45afc5 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
@@ -25,14 +25,14 @@ import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
-
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Choice;
-import java.util.Vector;
+import java.util.ArrayList;
public class CreateKeyDialogFragment extends DialogFragment {
@@ -78,7 +78,7 @@ public class CreateKeyDialogFragment extends DialogFragment {
boolean wouldBeMasterKey = (childCount == 0);
final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm);
- Vector<Choice> choices = new Vector<Choice>();
+ ArrayList<Choice> choices = new ArrayList<Choice>();
choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString(
R.string.dsa)));
if (!wouldBeMasterKey) {
@@ -114,21 +114,8 @@ public class CreateKeyDialogFragment extends DialogFragment {
public void onClick(DialogInterface di, int id) {
di.dismiss();
try {
- int nKeyIndex = keySize.getSelectedItemPosition();
- switch (nKeyIndex) {
- case 0:
- mNewKeySize = 512;
- break;
- case 1:
- mNewKeySize = 1024;
- break;
- case 2:
- mNewKeySize = 2048;
- break;
- case 3:
- mNewKeySize = 4096;
- break;
- }
+ final String selectedItem = (String) keySize.getSelectedItem();
+ mNewKeySize = Integer.parseInt(selectedItem);
} catch (NumberFormatException e) {
mNewKeySize = 0;
}
@@ -146,7 +133,26 @@ public class CreateKeyDialogFragment extends DialogFragment {
}
});
- return dialog.create();
+ final AlertDialog alertDialog = dialog.create();
+
+ final AdapterView.OnItemSelectedListener weakRsaListener = new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ final Choice selectedAlgorithm = (Choice)algorithm.getSelectedItem();
+ final int selectedKeySize = Integer.parseInt((String)keySize.getSelectedItem());
+ final boolean isWeakRsa = (selectedAlgorithm.getId() == Id.choice.algorithm.rsa && selectedKeySize <= 1024);
+ alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(!isWeakRsa);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ };
+
+ keySize.setOnItemSelectedListener(weakRsaListener);
+ algorithm.setOnItemSelectedListener(weakRsaListener);
+
+ return alertDialog;
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
index cd8bc79a9..b4c38184c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
@@ -17,10 +17,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
@@ -32,6 +28,9 @@ import android.os.Messenger;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.widget.Toast;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
public class DeleteFileDialogFragment extends DialogFragment {
private static final String ARG_DELETE_FILE = "delete_file";
@@ -67,7 +66,7 @@ public class DeleteFileDialogFragment extends DialogFragment {
alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFile));
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-
+
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
@@ -83,19 +82,23 @@ public class DeleteFileDialogFragment extends DialogFragment {
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance(
- R.string.progress_deleting_securely, ProgressDialog.STYLE_HORIZONTAL, false, null);
-
- // Message is received after deleting is done in ApgService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(activity, deletingDialog) {
+ getString(R.string.progress_deleting_securely),
+ ProgressDialog.STYLE_HORIZONTAL,
+ false,
+ null);
+
+ // Message is received after deleting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler =
+ new KeychainIntentServiceHandler(activity, deletingDialog) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
Toast.makeText(activity, R.string.file_delete_successful,
Toast.LENGTH_SHORT).show();
}
- };
+ }
};
// Create a new Messenger for the communication back
@@ -118,4 +121,4 @@ public class DeleteFileDialogFragment extends DialogFragment {
return alert.create();
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index dc40bab2a..3ff88aa2b 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-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
@@ -28,6 +28,11 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -41,140 +46,163 @@ import java.util.ArrayList;
public class DeleteKeyDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
- private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file";
- private static final String ARG_KEY_TYPE = "key_type";
+ private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_key_ring_row_ids";
public static final int MESSAGE_OKAY = 1;
+ public static final int MESSAGE_ERROR = 0;
- public static final String MESSAGE_NOT_DELETED = "not_deleted";
+ private boolean isSingleSelection = false;
+
+ private TextView mainMessage;
+ private CheckBox checkDeleteSecret;
+ private LinearLayout deleteSecretKeyView;
+ private View inflateView;
private Messenger mMessenger;
/**
* Creates new instance of this delete file dialog fragment
*/
- public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds,
- int keyType) {
+ public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds
+ ) {
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
args.putLongArray(ARG_DELETE_KEY_RING_ROW_IDS, keyRingRowIds);
- args.putInt(ARG_KEY_TYPE, keyType);
+ //We don't need the key type
frag.setArguments(args);
return frag;
}
- /**
- * Creates dialog
- */
+
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
+
final FragmentActivity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
final long[] keyRingRowIds = getArguments().getLongArray(ARG_DELETE_KEY_RING_ROW_IDS);
- final int keyType = getArguments().getInt(ARG_KEY_TYPE);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+
+ //Setup custom View to display in AlertDialog
+ LayoutInflater inflater = activity.getLayoutInflater();
+ inflateView = inflater.inflate(R.layout.view_key_delete_fragment, null);
+ builder.setView(inflateView);
+
+ deleteSecretKeyView = (LinearLayout) inflateView.findViewById(R.id.deleteSecretKeyView);
+ mainMessage = (TextView) inflateView.findViewById(R.id.mainMessage);
+ checkDeleteSecret = (CheckBox) inflateView.findViewById(R.id.checkDeleteSecret);
+
builder.setTitle(R.string.warning);
+ //If only a single key has been selected
if (keyRingRowIds.length == 1) {
Uri dataUri;
- if (keyType == Id.type.public_key) {
- dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowIds[0]));
+ ArrayList<Long> publicKeyRings; //Any one will do
+ isSingleSelection = true;
+
+ long selectedRow = keyRingRowIds[0];
+ long keyType;
+ publicKeyRings = ProviderHelper.getPublicKeyRingsRowIds(activity);
+
+ if (publicKeyRings.contains(selectedRow)) {
+ //TODO Should be a better method to do this other than getting all the KeyRings
+ dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(selectedRow));
+ keyType = Id.type.public_key;
} else {
- dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowIds[0]));
+ dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(selectedRow));
+ keyType = Id.type.secret_key;
}
+
String userId = ProviderHelper.getUserId(activity, dataUri);
+ //Hide the Checkbox and TextView since this is a single selection,user will be notified thru message
+ deleteSecretKeyView.setVisibility(View.GONE);
+ //Set message depending on which key it is.
+ mainMessage.setText(getString(keyType == Id.type.secret_key ? R.string.secret_key_deletion_confirmation
+ : R.string.public_key_deletetion_confirmation, userId));
+
- builder.setMessage(getString(
- keyType == Id.type.public_key ? R.string.key_deletion_confirmation
- : R.string.secret_key_deletion_confirmation, userId));
} else {
- builder.setMessage(R.string.key_deletion_confirmation_multi);
+ deleteSecretKeyView.setVisibility(View.VISIBLE);
+ mainMessage.setText(R.string.key_deletion_confirmation_multi);
}
+
builder.setIcon(R.drawable.ic_dialog_alert_holo_light);
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
-
@Override
- public void onClick(DialogInterface dialog, int id) {
- ArrayList<String> notDeleted = new ArrayList<String>();
-
- if (keyType == Id.type.public_key) {
- Uri queryUri = KeychainContract.KeyRings.buildPublicKeyRingsUri();
- String[] projection = new String[]{
- KeychainContract.KeyRings._ID, // 0
- KeychainContract.KeyRings.MASTER_KEY_ID, // 1
- KeychainContract.UserIds.USER_ID // 2
- };
-
- // make selection with all entries where _ID is one of the given row ids
- String selection = KeychainDatabase.Tables.KEY_RINGS + "." +
- KeychainContract.KeyRings._ID + " IN(";
- String selectionIDs = "";
- for (int i = 0; i < keyRingRowIds.length; i++) {
- selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'";
- if (i+1 < keyRingRowIds.length)
- selectionIDs += ",";
- }
- selection += selectionIDs + ")";
-
- Cursor cursor = activity.getContentResolver().query(queryUri, projection,
- selection, null, null);
-
- long rowId;
- long masterKeyId;
- String userId;
- try {
- while (cursor != null && cursor.moveToNext()) {
- rowId = cursor.getLong(0);
- masterKeyId = cursor.getLong(1);
- userId = cursor.getString(2);
-
- Log.d(Constants.TAG, "rowId: " + rowId + ", masterKeyId: " + masterKeyId
- + ", userId: " + userId);
-
- // check if a corresponding secret key exists...
- Cursor secretCursor = activity.getContentResolver().query(
- KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId)),
- null, null, null, null
- );
- if (secretCursor != null && secretCursor.getCount() > 0) {
- notDeleted.add(userId);
- } else {
- // it is okay to delete this key, no secret key found!
- ProviderHelper.deletePublicKeyRing(activity, rowId);
- }
- if (secretCursor != null) {
- secretCursor.close();
+ public void onClick(DialogInterface dialog, int which) {
+ Uri queryUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri();
+ String[] projection = new String[]{
+ KeychainContract.KeyRings.MASTER_KEY_ID, // 0
+ KeychainContract.KeyRings.TYPE// 1
+ };
+
+ // make selection with all entries where _ID is one of the given row ids
+ String selection = KeychainDatabase.Tables.KEY_RINGS + "." +
+ KeychainContract.KeyRings._ID + " IN(";
+ String selectionIDs = "";
+ for (int i = 0; i < keyRingRowIds.length; i++) {
+ selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'";
+ if (i + 1 < keyRingRowIds.length)
+ selectionIDs += ",";
+ }
+ selection += selectionIDs + ")";
+
+ Cursor cursor = activity.getContentResolver().query(queryUri, projection,
+ selection, null, null);
+
+
+ long masterKeyId;
+ long keyType;
+ boolean isSuccessfullyDeleted;
+ try {
+ isSuccessfullyDeleted = false;
+ while (cursor != null && cursor.moveToNext()) {
+ masterKeyId = cursor.getLong(0);
+ keyType = cursor.getLong(1);
+
+ Log.d(Constants.TAG, "masterKeyId: " + masterKeyId
+ + ", keyType:" + (keyType == KeychainContract.KeyTypes.PUBLIC ? "Public" : "Private"));
+
+
+ if (keyType == KeychainContract.KeyTypes.SECRET) {
+ if (checkDeleteSecret.isChecked() || isSingleSelection) {
+ ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), true);
}
- }
- } finally {
- if (cursor != null) {
- cursor.close();
+ } else {
+ ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), false);
}
}
- } else {
- for (long keyRowId : keyRingRowIds) {
- ProviderHelper.deleteSecretKeyRing(activity, keyRowId);
+
+ //Check if the selected rows have actually been deleted
+ cursor = activity.getContentResolver().query(queryUri, projection, selection, null, null);
+ if (cursor == null || cursor.getCount() == 0 || !checkDeleteSecret.isChecked()) {
+ isSuccessfullyDeleted = true;
+ }
+
+ } finally {
+ if (cursor != null) {
+ cursor.close();
}
+
}
dismiss();
- if (notDeleted.size() > 0) {
- Bundle data = new Bundle();
- data.putStringArrayList(MESSAGE_NOT_DELETED, notDeleted);
- sendMessageToHandler(MESSAGE_OKAY, data);
- } else {
+ if (isSuccessfullyDeleted) {
sendMessageToHandler(MESSAGE_OKAY, null);
+ } else {
+ sendMessageToHandler(MESSAGE_ERROR, null);
}
}
- });
+
+ }
+ );
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
@@ -196,7 +224,6 @@ public class DeleteKeyDialogFragment extends DialogFragment {
if (data != null) {
msg.setData(data);
}
-
try {
mMessenger.send(msg);
} catch (RemoteException e) {
@@ -205,4 +232,5 @@ public class DeleteKeyDialogFragment extends DialogFragment {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
+
} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
index 39ce63b5f..a4285c8e9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
@@ -17,11 +17,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.FileHelper;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -33,14 +28,16 @@ import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
-import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.FileHelper;
+import org.sufficientlysecure.keychain.util.Log;
public class FileDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
@@ -67,7 +64,7 @@ public class FileDialogFragment extends DialogFragment {
* Creates new instance of this file dialog fragment
*/
public static FileDialogFragment newInstance(Messenger messenger, String title, String message,
- String defaultFile, String checkboxText) {
+ String defaultFile, String checkboxText) {
FileDialogFragment frag = new FileDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
@@ -177,34 +174,33 @@ public class FileDialogFragment extends DialogFragment {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode & 0xFFFF) {
- case REQUEST_CODE: {
- if (resultCode == Activity.RESULT_OK && data != null) {
- try {
- String path = data.getData().getPath();
- Log.d(Constants.TAG, "path=" + path);
-
- // set filename used in export/import dialogs
- setFilename(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!", e);
+ case REQUEST_CODE: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ try {
+ String path = data.getData().getPath();
+ Log.d(Constants.TAG, "path=" + path);
+
+ // set filename used in export/import dialogs
+ setFilename(path);
+ } catch (NullPointerException e) {
+ Log.e(Constants.TAG, "Nullpointer while retrieving path!", e);
+ }
}
- }
- break;
- }
+ break;
+ }
- default:
- super.onActivityResult(requestCode, resultCode, data);
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
- break;
+ break;
}
}
/**
* Send message back to handler which is initialized in a activity
- *
- * @param what
- * Message integer you want to send
+ *
+ * @param what Message integer you want to send
*/
private void sendMessageToHandler(Integer what, Bundle data) {
Message msg = Message.obtain();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index c00232fd1..271219fa3 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -17,20 +17,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-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.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -52,6 +38,19 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+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.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.util.Log;
public class PassphraseDialogFragment extends DialogFragment implements OnEditorActionListener {
private static final String ARG_MESSENGER = "messenger";
@@ -62,20 +61,18 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
private Messenger mMessenger;
private EditText mPassphraseEditText;
- private boolean canKB;
+ private boolean mCanKB;
/**
* Creates new instance of this dialog fragment
- *
- * @param secretKeyId
- * secret key id you want to use
- * @param messenger
- * to communicate back after caching the passphrase
+ *
+ * @param secretKeyId secret key id you want to use
+ * @param messenger to communicate back after caching the passphrase
* @return
* @throws PgpGeneralException
*/
public static PassphraseDialogFragment newInstance(Context context, Messenger messenger,
- long secretKeyId) throws PgpGeneralException {
+ long secretKeyId) throws PgpGeneralException {
// check if secret key has a passphrase
if (!(secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none)) {
if (!PassphraseCacheService.hasPassphrase(context, secretKeyId)) {
@@ -131,7 +128,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
});
alert.setCancelable(false);
- canKB = false;
+ mCanKB = false;
return alert.create();
}
String userId = PgpKeyHelper.getMainUserIdSafe(activity, secretKey);
@@ -158,7 +155,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
PGPSecretKey clickSecretKey = secretKey;
if (clickSecretKey != null) {
- while (keyOK == true) {
+ while (keyOK) {
if (clickSecretKey != null) { // check again for loop
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
@@ -171,7 +168,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
Toast.makeText(activity,
R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show();
-
+
sendMessageToHandler(MESSAGE_CANCEL);
return;
} else {
@@ -187,14 +184,14 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
} catch (PGPException e) {
Toast.makeText(activity, R.string.wrong_passphrase,
Toast.LENGTH_SHORT).show();
-
+
sendMessageToHandler(MESSAGE_CANCEL);
return;
}
} else {
Toast.makeText(activity, R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show();
-
+
sendMessageToHandler(MESSAGE_CANCEL);
return; // ran out of keys to try
}
@@ -207,7 +204,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// cache the new passphrase
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
PassphraseCacheService.addCachedPassphrase(activity, keyId, passphrase);
- if (keyOK == false && clickSecretKey.getKeyID() != keyId) {
+ if (!keyOK && clickSecretKey.getKeyID() != keyId) {
PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(),
passphrase);
}
@@ -224,14 +221,14 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
});
- canKB = true;
+ mCanKB = true;
return alert.create();
}
@Override
public void onActivityCreated(Bundle arg0) {
super.onActivityCreated(arg0);
- if (canKB) {
+ if (mCanKB) {
// request focus and open soft keyboard
mPassphraseEditText.requestFocus();
getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
@@ -265,9 +262,8 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
/**
* Send message back to handler which is initialized in a activity
- *
- * @param what
- * Message integer you want to send
+ *
+ * @param what Message integer you want to send
*/
private void sendMessageToHandler(Integer what) {
Message msg = Message.obtain();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java
index 6c62d14e0..132a2ce86 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java
@@ -26,11 +26,10 @@ import android.content.DialogInterface.OnKeyListener;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.KeyEvent;
-
import org.sufficientlysecure.keychain.R;
public class ProgressDialogFragment extends DialogFragment {
- private static final String ARG_MESSAGE_ID = "message_id";
+ private static final String ARG_MESSAGE = "message";
private static final String ARG_STYLE = "style";
private static final String ARG_CANCELABLE = "cancelable";
@@ -39,16 +38,16 @@ public class ProgressDialogFragment extends DialogFragment {
/**
* Creates new instance of this fragment
*
- * @param messageId
+ * @param message
* @param style
* @param cancelable
* @return
*/
- public static ProgressDialogFragment newInstance(int messageId, int style, boolean cancelable,
+ public static ProgressDialogFragment newInstance(String message, int style, boolean cancelable,
OnCancelListener onCancelListener) {
ProgressDialogFragment frag = new ProgressDialogFragment();
Bundle args = new Bundle();
- args.putInt(ARG_MESSAGE_ID, messageId);
+ args.putString(ARG_MESSAGE, message);
args.putInt(ARG_STYLE, style);
args.putBoolean(ARG_CANCELABLE, cancelable);
@@ -101,8 +100,9 @@ public class ProgressDialogFragment extends DialogFragment {
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
- if (this.mOnCancelListener != null)
+ if (this.mOnCancelListener != null) {
this.mOnCancelListener.onCancel(dialog);
+ }
}
/**
@@ -117,22 +117,22 @@ public class ProgressDialogFragment extends DialogFragment {
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
- int messageId = getArguments().getInt(ARG_MESSAGE_ID);
+ String message = getArguments().getString(ARG_MESSAGE);
int style = getArguments().getInt(ARG_STYLE);
boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE);
- dialog.setMessage(getString(messageId));
+ dialog.setMessage(message);
dialog.setProgressStyle(style);
if (cancelable) {
dialog.setButton(DialogInterface.BUTTON_NEGATIVE,
activity.getString(R.string.progress_cancel),
new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- });
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
}
// Disable the back button
@@ -140,7 +140,6 @@ public class ProgressDialogFragment extends DialogFragment {
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
-
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
}
@@ -152,4 +151,4 @@ public class ProgressDialogFragment extends DialogFragment {
return dialog;
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
index e406547b3..ae61c1470 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
@@ -17,10 +17,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Log;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -40,6 +36,9 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener {
private static final String ARG_MESSENGER = "messenger";
@@ -55,11 +54,9 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
/**
* Creates new instance of this dialog fragment
- *
- * @param title
- * title of dialog
- * @param messenger
- * to communicate back after setting the passphrase
+ *
+ * @param title title of dialog
+ * @param messenger to communicate back after setting the passphrase
* @return
*/
public static SetPassphraseDialogFragment newInstance(Messenger messenger, int title) {
@@ -96,7 +93,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-
+
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
@@ -130,7 +127,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
});
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
-
+
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
@@ -168,9 +165,8 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
/**
* Send message back to handler which is initialized in a activity
- *
- * @param what
- * Message integer you want to send
+ *
+ * @param what Message integer you want to send
*/
private void sendMessageToHandler(Integer what, Bundle data) {
Message msg = Message.obtain();
@@ -187,4 +183,4 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
index b850638a6..741530b1d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
@@ -17,9 +17,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import org.sufficientlysecure.htmltextview.HtmlTextView;
-import org.sufficientlysecure.keychain.R;
-
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -31,6 +28,8 @@ import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
+import org.sufficientlysecure.htmltextview.HtmlTextView;
+import org.sufficientlysecure.keychain.R;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class ShareNfcDialogFragment extends DialogFragment {
@@ -97,4 +96,4 @@ public class ShareNfcDialogFragment extends DialogFragment {
return alert.create();
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
index f26cd35c0..94586810e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
@@ -28,7 +28,6 @@ import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@@ -91,7 +90,7 @@ public class ShareQrCodeDialogFragment extends DialogFragment {
alert.setPositiveButton(R.string.btn_okay, null);
byte[] fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), dataUri);
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FixedListView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FixedListView.java
index b9dcd0d25..da29f808a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FixedListView.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FixedListView.java
@@ -24,7 +24,7 @@ import android.widget.ListView;
/**
* Automatically calculate height of ListView based on contained items. This enables to put this
* ListView into a ScrollView without messing up.
- *
+ * <p/>
* from
* http://stackoverflow.com/questions/2419246/how-do-i-create-a-listview-thats-not-in-a-scrollview-
* or-has-the-scrollview-dis
@@ -52,4 +52,4 @@ public class FixedListView extends ListView {
super.onMeasure(widthMeasureSpec, expandSpec);
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
new file mode 100644
index 000000000..f9a5b92f3
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 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.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.beardedhen.androidbootstrap.FontAwesomeText;
+import org.sufficientlysecure.keychain.R;
+
+/**
+ * Class representing a LinearLayout that can fold and hide it's content when pressed
+ * To use just add the following to your xml layout
+
+ <org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ custom:foldedLabel="@string/TEXT_TO_DISPLAY_WHEN_FOLDED"
+ custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED"
+ custom:foldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_FOLDED"
+ custom:unFoldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_UNFOLDED">
+
+ <include layout="@layout/ELEMENTS_TO_BE_FOLDED"/>
+
+ </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout>
+
+ */
+public class FoldableLinearLayout extends LinearLayout {
+
+ private FontAwesomeText mFoldableIcon;
+ private boolean mFolded;
+ private boolean mHasMigrated = false;
+ private Integer mShortAnimationDuration = null;
+ private TextView mFoldableTextView = null;
+ private LinearLayout mFoldableContainer = null;
+ private View mFoldableLayout = null;
+
+ private String mFoldedIconName;
+ private String mUnFoldedIconName;
+ private String mFoldedLabel;
+ private String mUnFoldedLabel;
+
+ public FoldableLinearLayout(Context context) {
+ super(context);
+ processAttributes(context, null);
+ }
+
+ public FoldableLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ processAttributes(context, attrs);
+ }
+
+ public FoldableLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+ processAttributes(context, attrs);
+ }
+
+ /**
+ * Load given attributes to inner variables,
+ * @param context
+ * @param attrs
+ */
+ private void processAttributes(Context context, AttributeSet attrs) {
+ if(attrs != null) {
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.FoldableLinearLayout, 0, 0);
+ mFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_foldedIcon);
+ mUnFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_unFoldedIcon);
+ mFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_foldedLabel);
+ mUnFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_unFoldedLabel);
+ a.recycle();
+ }
+ // If any attribute isn't found then set a default one
+ mFoldedIconName = (mFoldedIconName == null) ? "fa-chevron-right" : mFoldedIconName;
+ mUnFoldedIconName = (mUnFoldedIconName == null) ? "fa-chevron-down" : mUnFoldedIconName;
+ mFoldedLabel = (mFoldedLabel == null) ? context.getString(R.id.none) : mFoldedLabel;
+ mUnFoldedLabel = (mUnFoldedLabel == null) ? context.getString(R.id.none) : mUnFoldedLabel;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ // if the migration has already happened
+ // there is no need to move any children
+ if(!mHasMigrated) {
+ migrateChildrenToContainer();
+ mHasMigrated = true;
+ }
+
+ initialiseInnerViews();
+
+ super.onFinishInflate();
+ }
+
+ /**
+ * Migrates Child views as declared in xml to the inner foldableContainer
+ */
+ private void migrateChildrenToContainer() {
+ // Collect children of FoldableLinearLayout as declared in XML
+ int childNum = getChildCount();
+ View[] children = new View[childNum];
+
+ for(int i = 0; i < childNum; i++) {
+ children[i] = getChildAt(i);
+ }
+ if(children[0].getId() == R.id.foldableControl) {
+
+ }
+
+ // remove all of them from FoldableLinearLayout
+ detachAllViewsFromParent();
+
+ // Inflate the inner foldable_linearlayout.xml
+ LayoutInflater inflator = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ mFoldableLayout = inflator.inflate(R.layout.foldable_linearlayout, this, true);
+ mFoldableContainer = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableContainer);
+
+ // Push previously collected children into foldableContainer.
+ for(int i = 0; i < childNum; i++) {
+ addView(children[i]);
+ }
+ }
+
+ private void initialiseInnerViews() {
+ mFoldableIcon = (FontAwesomeText) mFoldableLayout.findViewById(R.id.foldableIcon);
+ mFoldableIcon.setIcon(mFoldedIconName);
+ mFoldableTextView = (TextView) mFoldableLayout.findViewById(R.id.foldableText);
+ mFoldableTextView.setText(mFoldedLabel);
+
+ // retrieve and cache the system's short animation time
+ mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
+
+ LinearLayout foldableControl = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableControl);
+ foldableControl.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mFolded = !mFolded;
+ if (mFolded) {
+ mFoldableIcon.setIcon(mUnFoldedIconName);
+ mFoldableContainer.setVisibility(View.VISIBLE);
+ AlphaAnimation animation = new AlphaAnimation(0f, 1f);
+ animation.setDuration(mShortAnimationDuration);
+ mFoldableContainer.startAnimation(animation);
+ mFoldableTextView.setText(mUnFoldedLabel);
+
+ } else {
+ mFoldableIcon.setIcon(mFoldedIconName);
+ AlphaAnimation animation = new AlphaAnimation(1f, 0f);
+ animation.setDuration(mShortAnimationDuration);
+ animation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) { }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ // making sure that at the end the container is completely removed from view
+ mFoldableContainer.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) { }
+ });
+ mFoldableContainer.startAnimation(animation);
+ mFoldableTextView.setText(mFoldedLabel);
+ }
+ }
+ });
+
+ }
+
+ /**
+ * Adds provided child view to foldableContainer View
+ * @param child
+ */
+ @Override
+ public void addView(View child) {
+ if(mFoldableContainer != null) {
+ mFoldableContainer.addView(child);
+ }
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java
index bc60b2adf..6e1e4c678 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java
@@ -25,7 +25,7 @@ import android.util.AttributeSet;
* values should use {@link android.content.SharedPreferences#getInt}. When using XML-declared
* arrays for entry values, the arrays should be regular string arrays containing valid integer
* values.
- *
+ *
* @author Rodrigo Damazio
*/
public class IntegerListPreference extends ListPreference {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
index b1f97babf..0dfc6dc5e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
@@ -30,7 +30,7 @@ import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Choice;
-
+import android.annotation.TargetApi;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.content.Context;
@@ -47,8 +47,16 @@ import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.util.Choice;
+
+import java.text.DateFormat;
+import java.util.*;
public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private PGPSecretKey mKey;
@@ -85,7 +93,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private int mDatePickerResultCount = 0;
- private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = new DatePickerDialog.OnDateSetListener() {
+ private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
+ new DatePickerDialog.OnDateSetListener() {
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
// Note: Ignore results after the first one - android sends multiples.
if (mDatePickerResultCount++ == 0) {
@@ -139,6 +148,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
setExpiryDate(null);
mExpiryDateButton.setOnClickListener(new OnClickListener() {
+ @TargetApi(11)
public void onClick(View v) {
GregorianCalendar date = mExpiryDate;
if (date == null) {
@@ -169,16 +179,18 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
});
// setCalendarViewShown() is supported from API 11 onwards.
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB)
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
// Hide calendarView in tablets because of the unix warparound bug.
dialog.getDatePicker().setCalendarViewShown(false);
-
+ }
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
- if ( dialog != null && mCreatedDate != null ) {
- dialog.getDatePicker().setMinDate(mCreatedDate.getTime().getTime()+ DateUtils.DAY_IN_MILLIS);
+ if (dialog != null && mCreatedDate != null) {
+ dialog.getDatePicker()
+ .setMinDate(
+ mCreatedDate.getTime().getTime() + DateUtils.DAY_IN_MILLIS);
} else {
//When created date isn't available
- dialog.getDatePicker().setMinDate(date.getTime().getTime()+ DateUtils.DAY_IN_MILLIS);
+ dialog.getDatePicker().setMinDate(date.getTime().getTime() + DateUtils.DAY_IN_MILLIS);
}
}
@@ -208,9 +220,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(key));
- String keyId1Str = PgpKeyHelper.convertKeyIdToHex(key.getKeyID());
- String keyId2Str = PgpKeyHelper.convertKeyIdToHex(key.getKeyID() >> 32);
- mKeyId.setText(keyId1Str + " " + keyId2Str);
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyID());
+ mKeyId.setText(keyIdStr);
Vector<Choice> choices = new Vector<Choice>();
boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
@@ -353,9 +364,11 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
class ExpiryDatePickerDialog extends DatePickerDialog {
- public ExpiryDatePickerDialog(Context context, OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) {
+ public ExpiryDatePickerDialog(Context context, OnDateSetListener callBack,
+ int year, int monthOfYear, int dayOfMonth) {
super(context, callBack, year, monthOfYear, dayOfMonth);
}
+
//Set permanent title.
public void setTitle(CharSequence title) {
super.setTitle(getContext().getString(R.string.expiry_date_dialog_title));
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
index 47238cd56..171763672 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
@@ -16,8 +16,6 @@
package org.sufficientlysecure.keychain.ui.widget;
-import org.sufficientlysecure.keychain.R;
-
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
@@ -25,8 +23,8 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.sufficientlysecure.keychain.R;
public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
index 9758835d2..6c7737e6d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
@@ -48,9 +48,7 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.beardedhen.androidbootstrap.BootstrapButton;
-
import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -75,10 +73,10 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
private Choice mNewKeyAlgorithmChoice;
private int mNewKeySize;
- private boolean canEdit = true;
private boolean oldItemDeleted = false;
private ArrayList<String> mDeletedIDs = new ArrayList<String>();
private ArrayList<PGPSecretKey> mDeletedKeys = new ArrayList<PGPSecretKey>();
+ private boolean mCanEdit = true;
private ActionBarActivity mActivity;
@@ -105,30 +103,32 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
public void setType(int type) {
mType = type;
switch (type) {
- case Id.type.user_id: {
- mTitle.setText(R.string.section_user_ids);
- break;
- }
+ case Id.type.user_id: {
+ mTitle.setText(R.string.section_user_ids);
+ break;
+ }
- case Id.type.key: {
- mTitle.setText(R.string.section_keys);
- break;
- }
+ case Id.type.key: {
+ mTitle.setText(R.string.section_keys);
+ break;
+ }
- default: {
- break;
- }
+ default: {
+ break;
+ }
}
}
public void setCanEdit(boolean bCanEdit) {
- canEdit = bCanEdit;
- if (!canEdit) {
+ mCanEdit = bCanEdit;
+ if (!mCanEdit) {
mPlusButton.setVisibility(View.INVISIBLE);
}
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
protected void onFinishInflate() {
mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -146,7 +146,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
super.onFinishInflate();
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
public void onDeleted(Editor editor, boolean wasNewItem) {
oldItemDeleted |= !wasNewItem;
if (oldItemDeleted) {
@@ -262,39 +264,44 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
return mList;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
public void onClick(View v) {
- if (canEdit) {
+ if (mCanEdit) {
switch (mType) {
- case Id.type.user_id: {
- UserIdEditor view = (UserIdEditor) mInflater.inflate(
- R.layout.edit_key_user_id_item, mEditors, false);
- view.setEditorListener(this);
- view.setValue("", mEditors.getChildCount() == 0, true);
- mEditors.addView(view);
- if (mEditorListener != null) {
- mEditorListener.onEdited();
+ case Id.type.user_id: {
+ UserIdEditor view = (UserIdEditor) mInflater.inflate(
+ R.layout.edit_key_user_id_item, mEditors, false);
+ view.setEditorListener(this);
+ view.setValue("", mEditors.getChildCount() == 0, true);
+ mEditors.addView(view);
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ break;
}
- break;
- }
- case Id.type.key: {
- CreateKeyDialogFragment mCreateKeyDialogFragment = CreateKeyDialogFragment.newInstance(mEditors.getChildCount());
- mCreateKeyDialogFragment.setOnAlgorithmSelectedListener(new CreateKeyDialogFragment.OnAlgorithmSelectedListener() {
- @Override
- public void onAlgorithmSelected(Choice algorithmChoice, int keySize) {
- mNewKeyAlgorithmChoice = algorithmChoice;
- mNewKeySize = keySize;
- createKey();
- }
- });
- mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog");
- break;
- }
+ case Id.type.key: {
+ CreateKeyDialogFragment mCreateKeyDialogFragment =
+ CreateKeyDialogFragment.newInstance(mEditors.getChildCount());
+ mCreateKeyDialogFragment
+ .setOnAlgorithmSelectedListener(
+ new CreateKeyDialogFragment.OnAlgorithmSelectedListener() {
+ @Override
+ public void onAlgorithmSelected(Choice algorithmChoice, int keySize) {
+ mNewKeyAlgorithmChoice = algorithmChoice;
+ mNewKeySize = keySize;
+ createKey();
+ }
+ });
+ mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog");
+ break;
+ }
- default: {
- break;
- }
+ default: {
+ break;
+ }
}
this.updateEditorsVisible();
}
@@ -311,7 +318,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mEditors, false);
view.setEditorListener(this);
view.setValue(userId, mEditors.getChildCount() == 0, false);
- view.setCanEdit(canEdit);
+ view.setCanEdit(mCanEdit);
mEditors.addView(view);
}
@@ -332,7 +339,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
view.setEditorListener(this);
boolean isMasterKey = (mEditors.getChildCount() == 0);
view.setValue(list.get(i), isMasterKey, usages.get(i), newKeys);
- view.setCanEdit(canEdit);
+ view.setCanEdit(mCanEdit);
mEditors.addView(view);
}
@@ -367,19 +374,22 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// show progress dialog
- mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating,
- ProgressDialog.STYLE_SPINNER, true, new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- mActivity.stopService(intent);
- }
- });
+ mGeneratingDialog = ProgressDialogFragment.newInstance(
+ getResources().getQuantityString(R.plurals.progress_generating, 1),
+ ProgressDialog.STYLE_SPINNER,
+ true,
+ new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mActivity.stopService(intent);
+ }
+ });
- // Message is received after generating is done in ApgService
+ // Message is received after generating is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity,
mGeneratingDialog) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -390,7 +400,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
addGeneratedKeyToView(newKey);
}
- };
+ }
};
// Create a new Messenger for the communication back
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java
index 752d44f89..937a48e48 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java
@@ -26,7 +26,7 @@ import android.widget.TextView;
/**
* Copied from StickyListHeaders lib example
- *
+ *
* @author Eric Frohnhoefer
*/
public class UnderlineTextView extends TextView {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
index 8760b72f7..d4b15613a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
@@ -21,19 +21,18 @@ import java.util.regex.Pattern;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
+import android.util.Patterns;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-
+import android.widget.*;
import com.beardedhen.androidbootstrap.BootstrapButton;
+import org.sufficientlysecure.keychain.helper.ContactHelper;
+
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
@@ -43,21 +42,13 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
private String mOriginalID;
private EditText mName;
private String mOriginalName;
- private EditText mEmail;
+ private AutoCompleteTextView mEmail;
private String mOriginalEmail;
private EditText mComment;
private String mOriginalComment;
private boolean mOriginallyMainUserID;
private boolean mIsNewId;
- // see http://www.regular-expressions.info/email.html
- // RFC 2822 if we omit the syntax using double quotes and square brackets
- // android.util.Patterns.EMAIL_ADDRESS is only available as of Android 2.2+
- private static final Pattern EMAIL_PATTERN = Pattern
- .compile(
- "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?",
- Pattern.CASE_INSENSITIVE);
-
public void setCanEdit(boolean bCanEdit) {
if (!bCanEdit) {
mDeleteButton.setVisibility(View.INVISIBLE);
@@ -114,11 +105,46 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
mName = (EditText) findViewById(R.id.name);
mName.addTextChangedListener(mTextWatcher);
- mEmail = (EditText) findViewById(R.id.email);
- mEmail.addTextChangedListener(mTextWatcher);
+ mEmail = (AutoCompleteTextView) findViewById(R.id.email);
mComment = (EditText) findViewById(R.id.comment);
mComment.addTextChangedListener(mTextWatcher);
+
+ mEmail.setThreshold(1); // Start working from first character
+ mEmail.setAdapter(
+ new ArrayAdapter<String>
+ (this.getContext(), android.R.layout.simple_dropdown_item_1line,
+ ContactHelper.getMailAccounts(getContext())
+ ));
+ mEmail.addTextChangedListener(new TextWatcher(){
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ String email = editable.toString();
+ if (email.length() > 0) {
+ Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
+ if (emailMatcher.matches()) {
+ mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0,
+ android.R.drawable.presence_online, 0);
+ } else {
+ mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0,
+ android.R.drawable.presence_offline, 0);
+ }
+ } else {
+ // remove drawable if email is empty
+ mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+ }
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+ });
+
super.onFinishInflate();
}
@@ -151,19 +177,11 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
setIsMainUserId(isMainID);
}
- public String getValue() throws InvalidEmailException {
+ public String getValue() {
String name = ("" + mName.getText()).trim();
String email = ("" + mEmail.getText()).trim();
String comment = ("" + mComment.getText()).trim();
- if (email.length() > 0) {
- Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
- if (!emailMatcher.matches()) {
- throw new InvalidEmailException(getContext().getString(R.string.error_invalid_email,
- email));
- }
- }
-
String userId = name;
if (comment.length() > 0) {
userId += " (" + comment + ")";
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
index d3c37edc6..d2f4cc003 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
@@ -17,13 +17,14 @@
package org.sufficientlysecure.keychain.util;
-import java.util.HashMap;
+import android.annotation.SuppressLint;
+import android.app.Activity;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import android.annotation.SuppressLint;
-import android.app.Activity;
+
+import java.util.HashMap;
@SuppressLint("UseSparseArrays")
public class AlgorithmNames {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
index 9e0042c00..1a6184d9c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
@@ -39,7 +39,7 @@ public class Choice {
}
@Override
- public String toString() {
+ public String toString() {
return mName;
}
}
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 921d22f21..b987e1533 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,6 @@
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 org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@@ -47,19 +29,22 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
+import org.sufficientlysecure.keychain.Constants;
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;
+
+import static org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry.getAlgorithmFromId;
-/**
- * TODO:
- * rewrite to use machine readable output.
- * <p/>
- * see http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5
- * https://github.com/openpgp-keychain/openpgp-keychain/issues/259
- */
public class HkpKeyServer extends KeyServer {
private static class HttpError extends Exception {
private static final long serialVersionUID = 1718783705229428893L;
@@ -82,21 +67,86 @@ public class HkpKeyServer extends KeyServer {
}
private String mHost;
- private short mPort = 11371;
-
- // example:
- // pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a
- // href="/pks/lookup?op=vindex&search=0x887DF4BE9F5C9090">Jörg Runge
- // &lt;joerg@joergrunge.de&gt;</a>
- public static Pattern PUB_KEY_LINE = Pattern
- .compile(
- "pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)",
+ private short mPort;
+
+ /**
+ * pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags%
+ * <ul>
+ * <li>%<b>keyid</b>% = this is either the fingerprint or the key ID of the key. Either the 16-digit or 8-digit
+ * key IDs are acceptable, but obviously the fingerprint is best.</li>
+ * <li>%<b>algo</b>% = the algorithm number, (i.e. 1==RSA, 17==DSA, etc).
+ * See <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a></li>
+ * <li>%<b>keylen</b>% = the key length (i.e. 1024, 2048, 4096, etc.)</li>
+ * <li>%<b>creationdate</b>% = creation date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of seconds since
+ * 1/1/1970 UTC time)</li>
+ * <li>%<b>expirationdate</b>% = expiration date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of seconds since
+ * 1/1/1970 UTC time)</li>
+ * <li>%<b>flags</b>% = letter codes to indicate details of the key, if any. Flags may be in any order. The
+ * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so
+ * the absence of a given flag does not necessarily mean the absence of the detail.
+ * <ul>
+ * <li>r == revoked</li>
+ * <li>d == disabled</li>
+ * <li>e == expired</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @see <a href="http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5.2">5.2. Machine Readable Indexes</a>
+ * in Internet-Draft OpenPGP HTTP Keyserver Protocol Document
+ */
+ public static final Pattern PUB_KEY_LINE = Pattern
+ .compile("pub:([0-9a-fA-F]+):([0-9]+):([0-9]+):([0-9]+):([0-9]*):([rde]*)[ \n\r]*" // pub line
+ + "(uid:(.*):([0-9]+):([0-9]*):([rde]*))+", // one or more uid lines
Pattern.CASE_INSENSITIVE);
- public static Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE
- | Pattern.CASE_INSENSITIVE);
- public HkpKeyServer(String host) {
+ /**
+ * uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags%
+ * <ul>
+ * <li>%<b>escaped uid string</b>% = the user ID string, with HTTP %-escaping for anything that isn't 7-bit
+ * safe as well as for the ":" character. Any other characters may be escaped, as desired.</li>
+ * <li>%<b>creationdate</b>% = creation date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of seconds since
+ * 1/1/1970 UTC time)</li>
+ * <li>%<b>expirationdate</b>% = expiration date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of seconds since
+ * 1/1/1970 UTC time)</li>
+ * <li>%<b>flags</b>% = letter codes to indicate details of the key, if any. Flags may be in any order. The
+ * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so
+ * the absence of a given flag does not necessarily mean the absence of the detail.
+ * <ul>
+ * <li>r == revoked</li>
+ * <li>d == disabled</li>
+ * <li>e == expired</li>
+ * </ul>
+ * </li>
+ * </ul>
+ */
+ public static final Pattern UID_LINE = Pattern
+ .compile("uid:(.*):([0-9]+):([0-9]*):([rde]*)",
+ Pattern.CASE_INSENSITIVE);
+
+ 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) {
@@ -104,7 +154,7 @@ public class HkpKeyServer extends KeyServer {
mPort = port;
}
- static private String readAll(InputStream in, String encoding) throws IOException {
+ private static String readAll(InputStream in, String encoding) throws IOException {
ByteArrayOutputStream raw = new ByteArrayOutputStream();
byte buffer[] = new byte[1 << 16];
@@ -129,6 +179,7 @@ public class HkpKeyServer extends KeyServer {
for (int i = 0; i < ips.length; ++i) {
try {
String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request;
+ Log.d(Constants.TAG, "hkp keyserver query: " + url);
URL realUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
conn.setConnectTimeout(5000);
@@ -166,9 +217,9 @@ public class HkpKeyServer extends KeyServer {
} catch (UnsupportedEncodingException e) {
return null;
}
- String request = "/pks/lookup?op=index&search=" + encodedQuery;
+ String request = "/pks/lookup?op=index&options=mr&search=" + encodedQuery;
- String data = null;
+ String data;
try {
data = query(request);
} catch (HttpError e) {
@@ -186,48 +237,65 @@ public class HkpKeyServer extends KeyServer {
throw new QueryException("querying server(s) for '" + mHost + "' failed");
}
- Matcher matcher = PUB_KEY_LINE.matcher(data);
+ final Matcher matcher = PUB_KEY_LINE.matcher(data);
while (matcher.find()) {
- ImportKeysListEntry info = new ImportKeysListEntry();
- info.bitStrength = Integer.parseInt(matcher.group(1));
- info.algorithm = matcher.group(2);
- info.hexKeyId = "0x" + matcher.group(3);
- info.keyId = PgpKeyHelper.convertHexToKeyId(matcher.group(3));
- String chunks[] = matcher.group(4).split("-");
-
- GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
- tmpGreg.set(Integer.parseInt(chunks[0]), Integer.parseInt(chunks[1]),
- Integer.parseInt(chunks[2]));
- info.date = tmpGreg.getTime();
- info.userIds = new ArrayList<String>();
- if (matcher.group(5).startsWith("*** KEY")) {
- info.revoked = true;
+ final ImportKeysListEntry entry = new ImportKeysListEntry();
+
+ entry.setBitStrength(Integer.parseInt(matcher.group(3)));
+
+ final int algorithmId = Integer.decode(matcher.group(2));
+ entry.setAlgorithm(getAlgorithmFromId(algorithmId));
+
+ // group 1 contains the full fingerprint (v4) or the long key id if available
+ // see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff
+ // and https://github.com/openpgp-keychain/openpgp-keychain/issues/259#issuecomment-38168176
+ String fingerprintOrKeyId = matcher.group(1);
+ if (fingerprintOrKeyId.length() > 16) {
+ entry.setFingerPrintHex(fingerprintOrKeyId.toLowerCase(Locale.US));
+ entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
+ - 16, fingerprintOrKeyId.length()));
} else {
- String tmp = matcher.group(5).replaceAll("<.*?>", "");
- tmp = Html.fromHtml(tmp).toString();
- info.userIds.add(tmp);
+ // set key id only
+ entry.setKeyIdHex("0x" + fingerprintOrKeyId);
}
- if (matcher.group(6).length() > 0) {
- Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6));
- while (matcher2.find()) {
- String tmp = matcher2.group(1).replaceAll("<.*?>", "");
- tmp = Html.fromHtml(tmp).toString();
- info.userIds.add(tmp);
+
+ final long creationDate = Long.parseLong(matcher.group(4));
+ final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ tmpGreg.setTimeInMillis(creationDate * 1000);
+ entry.setDate(tmpGreg.getTime());
+
+ entry.setRevoked(matcher.group(6).contains("r"));
+
+ ArrayList<String> userIds = new ArrayList<String>();
+ final String uidLines = matcher.group(7);
+ final Matcher uidMatcher = UID_LINE.matcher(uidLines);
+ while (uidMatcher.find()) {
+ String tmp = uidMatcher.group(1).trim();
+ if (tmp.contains("%")) {
+ try {
+ // converts Strings like "Universit%C3%A4t" to a proper encoding form "Universität".
+ tmp = (URLDecoder.decode(tmp, "UTF8"));
+ } catch (UnsupportedEncodingException ignored) {
+ // will never happen, because "UTF8" is supported
+ }
}
+ userIds.add(tmp);
}
- results.add(info);
- }
+ entry.setUserIds(userIds);
+ results.add(entry);
+ }
return results;
}
@Override
- public String get(long keyId) throws QueryException {
+ public String get(String keyIdHex) throws QueryException {
HttpClient client = new DefaultHttpClient();
try {
- HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
- + "/pks/lookup?op=get&search=0x" + PgpKeyHelper.convertKeyToHex(keyId));
-
+ String query = "http://" + mHost + ":" + mPort +
+ "/pks/lookup?op=get&options=mr&search=" + keyIdHex;
+ Log.d(Constants.TAG, "hkp keyserver get: " + query);
+ HttpGet get = new HttpGet(query);
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new QueryException("not found");
@@ -250,13 +318,14 @@ public class HkpKeyServer extends KeyServer {
}
@Override
- public void add(String armoredText) throws AddKeyException {
+ public void add(String armoredKey) throws AddKeyException {
HttpClient client = new DefaultHttpClient();
try {
- HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add");
-
+ String query = "http://" + mHost + ":" + mPort + "/pks/add";
+ HttpPost post = new HttpPost(query);
+ Log.d(Constants.TAG, "hkp keyserver add: " + query);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
- nameValuePairs.add(new BasicNameValuePair("keytext", armoredText));
+ nameValuePairs.add(new BasicNameValuePair("keytext", armoredKey));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
index a43c03e3e..b95b3ee6a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
@@ -18,30 +18,28 @@ package org.sufficientlysecure.keychain.util;
import android.content.Intent;
import android.support.v4.app.Fragment;
-
import com.google.zxing.integration.android.IntentIntegrator;
/**
* IntentIntegrator for the V4 Android compatibility package.
- *
+ *
* @author Lachezar Dobrev
*/
public final class IntentIntegratorSupportV4 extends IntentIntegrator {
- private final Fragment fragment;
+ private final Fragment mFragment;
/**
- * @param fragment
- * Fragment to handle activity response.
+ * @param fragment Fragment to handle activity response.
*/
public IntentIntegratorSupportV4(Fragment fragment) {
super(fragment.getActivity());
- this.fragment = fragment;
+ this.mFragment = fragment;
}
@Override
protected void startActivityForResult(Intent intent, int code) {
- fragment.startActivityForResult(intent, code);
+ mFragment.startActivityForResult(intent, code);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java
index caaa07524..40105df4f 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java
@@ -16,13 +16,21 @@
package org.sufficientlysecure.keychain.util;
+import java.util.ArrayList;
import java.util.Iterator;
public class IterableIterator<T> implements Iterable<T> {
private Iterator<T> mIter;
- public IterableIterator(Iterator<T> iter) {
+ public IterableIterator(Iterator<T> iter, boolean failsafe) {
mIter = iter;
+ if(failsafe && mIter == null) {
+ // is there a better way?
+ mIter = new ArrayList<T>().iterator();
+ }
+ }
+ public IterableIterator(Iterator<T> iter) {
+ this(iter, false);
}
public Iterator<T> iterator() {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
index 7049820e8..7f70867a5 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
@@ -18,12 +18,12 @@
package org.sufficientlysecure.keychain.util;
-import java.util.List;
-
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
+import java.util.List;
+
public abstract class KeyServer {
- static public class QueryException extends Exception {
+ public static class QueryException extends Exception {
private static final long serialVersionUID = 2703768928624654512L;
public QueryException(String message) {
@@ -31,22 +31,22 @@ public abstract class KeyServer {
}
}
- static public class TooManyResponses extends Exception {
+ public static class TooManyResponses extends Exception {
private static final long serialVersionUID = 2703768928624654513L;
}
- static public class InsufficientQuery extends Exception {
+ public static class InsufficientQuery extends Exception {
private static final long serialVersionUID = 2703768928624654514L;
}
- static public class AddKeyException extends Exception {
+ public static class AddKeyException extends Exception {
private static final long serialVersionUID = -507574859137295530L;
}
abstract List<ImportKeysListEntry> search(String query) throws QueryException, TooManyResponses,
InsufficientQuery;
- abstract String get(long keyId) throws QueryException;
+ abstract String get(String keyIdHex) throws QueryException;
- abstract void add(String armoredText) throws AddKeyException;
+ abstract void add(String armoredKey) throws AddKeyException;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
new file mode 100644
index 000000000..14b2a2211
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sufficientlysecure.keychain.util;
+
+public interface KeychainServiceListener {
+ boolean hasServiceStopped();
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java
index bcf275c32..f58f1757a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java
@@ -21,7 +21,6 @@ import org.sufficientlysecure.keychain.Constants;
/**
* Wraps Android Logging to enable or disable debug output using Constants
- *
*/
public final class Log {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PRNGFixes.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PRNGFixes.java
index 530a81044..2d8fbcd81 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PRNGFixes.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PRNGFixes.java
@@ -14,48 +14,36 @@ import android.os.Build;
import android.os.Process;
import android.util.Log;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.security.NoSuchAlgorithmException;
-import java.security.Provider;
-import java.security.SecureRandom;
-import java.security.SecureRandomSpi;
-import java.security.Security;
+import java.io.*;
+import java.security.*;
/**
* Fixes for the output of the default PRNG having low entropy.
- *
+ * <p/>
* The fixes need to be applied via {@link #apply()} before any use of Java Cryptography
* Architecture primitives. A good place to invoke them is in the application's {@code onCreate}.
- *
+ * <p/>
* copied from http://android-developers.blogspot.de/2013/08/some-securerandom-thoughts.html
- *
- *
+ * <p/>
+ * <p/>
* More information on these Android bugs:
* http://blog.k3170makan.com/2013/08/more-details-on-android-jca-prng-flaw.html
* Paper: "Randomly failed! Weaknesses in Java Pseudo Random Number Generators (PRNGs)"
- *
- *
+ * <p/>
+ * <p/>
* Sep 15, 2013:
* On some devices /dev/urandom is non-writable!
* No need to seed /dev/urandom. urandom should have enough seeds from the OS and kernel.
* Only OpenSSL seeds are broken. See http://emboss.github.io/blog/2013/08/21/openssl-prng-is-not-really-fork-safe
- *
+ * <p/>
* see also:
* https://github.com/k9mail/k-9/commit/dda8f64276d4d29c43f86237cd77819c28f22f21
* In addition to a couple of custom ROMs linking /dev/urandom to a non-writable
* random version, now Samsung's SELinux policy also prevents apps from opening
* /dev/urandom for writing. Since we shouldn't need to write to /dev/urandom anyway
* we now simply don't.
- *
- *
+ * <p/>
+ * <p/>
* Sep 17, 2013:
* Updated from official blogpost:
* Update: the original code sample below crashed on a small fraction of Android
@@ -66,10 +54,13 @@ public final class PRNGFixes {
private static final int VERSION_CODE_JELLY_BEAN = 16;
private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
- getBuildFingerprintAndDeviceSerial();
+ getBuildFingerprintAndDeviceSerial();
- /** Hidden constructor to prevent instantiation. */
- private PRNGFixes() {}
+ /**
+ * Hidden constructor to prevent instantiation.
+ */
+ private PRNGFixes() {
+ }
/**
* Applies all fixes.
@@ -136,7 +127,7 @@ public final class PRNGFixes {
if ((secureRandomProviders == null)
|| (secureRandomProviders.length < 1)
|| (!LinuxPRNGSecureRandomProvider.class.equals(
- secureRandomProviders[0].getClass()))) {
+ secureRandomProviders[0].getClass()))) {
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
}
@@ -161,7 +152,7 @@ public final class PRNGFixes {
rng2.getProvider().getClass())) {
throw new SecurityException(
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
- + " Provider: " + rng2.getProvider().getClass());
+ + " Provider: " + rng2.getProvider().getClass());
}
}
@@ -175,7 +166,7 @@ public final class PRNGFixes {
super("LinuxPRNG",
1.0,
"A Linux-specific random number provider that uses"
- + " /dev/urandom");
+ + " /dev/urandom");
// Although /dev/urandom is not a SHA-1 PRNG, some apps
// explicitly request a SHA1PRNG SecureRandom and we thus need to
// prevent them from getting the default implementation whose output
@@ -358,4 +349,4 @@ public final class PRNGFixes {
throw new RuntimeException("UTF-8 encoding not supported");
}
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
index aa21581c6..377a8d5d6 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
@@ -17,11 +17,7 @@
package org.sufficientlysecure.keychain.util;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@@ -32,59 +28,63 @@ import java.util.concurrent.locks.ReentrantLock;
public class PausableThreadPoolExecutor extends ThreadPoolExecutor {
public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
- TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
+ TimeUnit unit, BlockingQueue<Runnable> workQueue,
+ RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
- TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
- RejectedExecutionHandler handler) {
+ TimeUnit unit, BlockingQueue<Runnable> workQueue,
+ ThreadFactory threadFactory,
+ RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
- TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
+ TimeUnit unit, BlockingQueue<Runnable> workQueue,
+ ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
- TimeUnit unit, BlockingQueue<Runnable> workQueue) {
+ TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
- private boolean isPaused;
- private ReentrantLock pauseLock = new ReentrantLock();
- private Condition unpaused = pauseLock.newCondition();
+ private boolean mIsPaused;
+ private ReentrantLock mPauseLock = new ReentrantLock();
+ private Condition mUnPaused = mPauseLock.newCondition();
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
- pauseLock.lock();
+ mPauseLock.lock();
try {
- while (isPaused)
- unpaused.await();
+ while (mIsPaused) {
+ mUnPaused.await();
+ }
} catch (InterruptedException ie) {
t.interrupt();
} finally {
- pauseLock.unlock();
+ mPauseLock.unlock();
}
}
public void pause() {
- pauseLock.lock();
+ mPauseLock.lock();
try {
- isPaused = true;
+ mIsPaused = true;
} finally {
- pauseLock.unlock();
+ mPauseLock.unlock();
}
}
public void resume() {
- pauseLock.lock();
+ mPauseLock.lock();
try {
- isPaused = false;
- unpaused.signalAll();
+ mIsPaused = false;
+ mUnPaused.signalAll();
} finally {
- pauseLock.unlock();
+ mPauseLock.unlock();
}
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Primes.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Primes.java
index f503227a3..28a12bf37 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Primes.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/Primes.java
@@ -25,147 +25,147 @@ public final class Primes {
// taken from http://www.ietf.org/rfc/rfc3526.txt
public static final String P1536 =
"FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF";
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF";
public static final String P2048 =
"FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AACAA68 FFFFFFFF FFFFFFFF";
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AACAA68 FFFFFFFF FFFFFFFF";
public static final String P3072 =
"FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
- "43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF";
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF";
public static final String P4096 =
"FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
- "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
- "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
- "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
- "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
- "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
- "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199" +
- "FFFFFFFF FFFFFFFF";
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
+ "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
+ "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
+ "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
+ "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
+ "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199" +
+ "FFFFFFFF FFFFFFFF";
public static final String P6144 =
"FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
- "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
- "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
- "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
- "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
- "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
- "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
- "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
- "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
- "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
- "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
- "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
- "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
- "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
- "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
- "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
- "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
- "12BF2D5B 0B7474D6 E694F91E 6DCC4024 FFFFFFFF FFFFFFFF";
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
+ "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
+ "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
+ "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
+ "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
+ "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
+ "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
+ "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
+ "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
+ "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
+ "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
+ "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
+ "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
+ "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
+ "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
+ "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
+ "12BF2D5B 0B7474D6 E694F91E 6DCC4024 FFFFFFFF FFFFFFFF";
public static final String P8192 =
"FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
- "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
- "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
- "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
- "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
- "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
- "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
- "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
- "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
- "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
- "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
- "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
- "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
- "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
- "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
- "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
- "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
- "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
- "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
- "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
- "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
- "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
- "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
- "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
- "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
- "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
- "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
- "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
- "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
- "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
- "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
- "12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4" +
- "38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300" +
- "741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568" +
- "3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9" +
- "22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B" +
- "4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A" +
- "062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36" +
- "4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1" +
- "B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92" +
- "4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47" +
- "9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71" +
- "60C980DD 98EDD3DF FFFFFFFF FFFFFFFF";
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
+ "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
+ "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
+ "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
+ "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
+ "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
+ "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
+ "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
+ "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
+ "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
+ "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
+ "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
+ "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
+ "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
+ "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
+ "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
+ "12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4" +
+ "38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300" +
+ "741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568" +
+ "3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9" +
+ "22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B" +
+ "4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A" +
+ "062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36" +
+ "4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1" +
+ "B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92" +
+ "4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47" +
+ "9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71" +
+ "60C980DD 98EDD3DF FFFFFFFF FFFFFFFF";
public static BigInteger getBestPrime(int keySize) {
String primeString;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
index 9e8118e7a..8c3367bea 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
@@ -18,26 +18,24 @@
package org.sufficientlysecure.keychain.util;
-import java.util.Hashtable;
-
-import org.sufficientlysecure.keychain.Constants;
-
import android.graphics.Bitmap;
import android.graphics.Color;
-
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import org.sufficientlysecure.keychain.Constants;
+
+import java.util.Hashtable;
public class QrCodeUtils {
- public final static QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
+ public static final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
/**
* Generate Bitmap with QR Code based on input.
- *
+ *
* @param input
* @param size
* @return QR Code as Bitmap