aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-03-02 14:39:28 +0100
committerVincent Breitmoser <valodim@mugenguild.com>2015-03-02 14:39:28 +0100
commit2f83291920e024b0f8038fe0caa747051b41cf1c (patch)
tree780fc0975e56b3dbd8a1eb4cb2988ac3e2e23ea1 /OpenKeychain/src/main/java/org/sufficientlysecure
parentaa22a0defcf94d5f734d3a00288f5c8d916d2e57 (diff)
parent4e29d027af05fc574dc5398d2fb3afcdf3defc70 (diff)
downloadopen-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.tar.gz
open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.tar.bz2
open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.zip
NON-WORKING Merge branch 'development' into linked-identities
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java OpenKeychain/src/main/res/layout/view_key_main_fragment.xml OpenKeychain/src/main/res/values/strings.xml extern/spongycastle
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java58
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java103
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java136
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java72
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java150
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java139
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PromoteKeyResult.java53
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java90
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SingletonResult.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java490
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java132
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java176
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java)488
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java135
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java16
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java99
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java62
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java124
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java609
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java140
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java292
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java)2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java36
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java350
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java404
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java599
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java15
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java67
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java131
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedActivity.java)55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java198
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java88
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java125
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java306
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java36
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java38
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java150
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java69
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java73
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java29
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java229
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java)161
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java229
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java458
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java86
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java71
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java53
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java70
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java145
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java)56
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java)25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java716
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java257
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvCertsFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java)72
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java)77
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java125
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java184
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java232
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java369
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java460
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java16
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java)49
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java97
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ActionBarHelper.java99
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java101
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java149
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/AspectRatioImageView.java138
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java39
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ListAwareSwipeRefreshLayout.java104
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java318
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java211
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java42
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FabContainer.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Iso7816TLV.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java15
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java11
160 files changed, 7662 insertions, 5274 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 04a932658..67fa30a44 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -20,11 +20,6 @@ package org.sufficientlysecure.keychain;
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.EncryptFilesActivity;
-import org.sufficientlysecure.keychain.ui.EncryptTextActivity;
-import org.sufficientlysecure.keychain.ui.KeyListActivity;
import java.io.File;
@@ -91,21 +86,6 @@ public final class Constants {
public static final int PREF_VERSION = 4;
}
- public static final class DrawerItems {
- public static final Class KEY_LIST = KeyListActivity.class;
- public static final Class ENCRYPT_TEXT = EncryptTextActivity.class;
- public static final Class ENCRYPT_FILE = EncryptFilesActivity.class;
- public static final Class DECRYPT = DecryptActivity.class;
- public static final Class REGISTERED_APPS_LIST = AppsListActivity.class;
- public static final Class[] ARRAY = new Class[]{
- KEY_LIST,
- ENCRYPT_TEXT,
- ENCRYPT_FILE,
- DECRYPT,
- REGISTERED_APPS_LIST
- };
- }
-
public static final class key {
public static final int none = 0;
public static final int symmetric = -1;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index 446699a81..01f6735ea 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -32,12 +32,12 @@ import android.provider.ContactsContract;
import android.widget.Toast;
import org.spongycastle.jce.provider.BouncyCastleProvider;
-import org.sufficientlysecure.keychain.util.Preferences;
-import org.sufficientlysecure.keychain.util.TlsHelper;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PRNGFixes;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.TlsHelper;
import java.security.Security;
@@ -86,7 +86,7 @@ public class KeychainApplication extends Application {
}
brandGlowEffect(getApplicationContext(),
- getApplicationContext().getResources().getColor(R.color.emphasis));
+ getApplicationContext().getResources().getColor(R.color.primary));
setupAccountAsNeeded(this);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
index 255ca1cde..c0221fad3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
@@ -17,8 +17,8 @@
package org.sufficientlysecure.keychain.keyimport;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
import java.util.Vector;
@@ -32,10 +32,10 @@ public class CloudSearch {
public static ArrayList<ImportKeysListEntry> search(final String query, Preferences.CloudSearchPrefs cloudPrefs)
throws Keyserver.CloudSearchFailureException {
- final ArrayList<Keyserver> servers = new ArrayList<Keyserver>();
+ final ArrayList<Keyserver> servers = new ArrayList<>();
// it's a Vector for sync, multiple threads might report problems
- final Vector<Keyserver.CloudSearchFailureException> problems = new Vector<Keyserver.CloudSearchFailureException>();
+ final Vector<Keyserver.CloudSearchFailureException> problems = new Vector<>();
if (cloudPrefs.searchKeyserver) {
servers.add(new HkpKeyserver(cloudPrefs.keyserver));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
index 7f07b8162..480319081 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
@@ -19,10 +19,10 @@
package org.sufficientlysecure.keychain.keyimport;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.TlsHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.TlsHelper;
import java.io.BufferedWriter;
import java.io.IOException;
@@ -234,7 +234,7 @@ public class HkpKeyserver extends Keyserver {
@Override
public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException,
QueryNeedsRepairException {
- ArrayList<ImportKeysListEntry> results = new ArrayList<ImportKeysListEntry>();
+ ArrayList<ImportKeysListEntry> results = new ArrayList<>();
if (query.length() < 3) {
throw new QueryTooShortException();
@@ -305,7 +305,7 @@ public class HkpKeyserver extends Keyserver {
entry.setRevoked(matcher.group(6).contains("r"));
entry.setExpired(matcher.group(6).contains("e"));
- ArrayList<String> userIds = new ArrayList<String>();
+ ArrayList<String> userIds = new ArrayList<>();
final String uidLines = matcher.group(7);
final Matcher uidMatcher = UID_LINE.matcher(uidLines);
while (uidMatcher.find()) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
index d60d7757b..79065604a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
@@ -23,9 +23,9 @@ import android.os.Parcelable;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.io.Serializable;
import java.util.ArrayList;
@@ -89,7 +89,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
public ImportKeysListEntry createFromParcel(final Parcel source) {
ImportKeysListEntry vr = new ImportKeysListEntry();
vr.mPrimaryUserId = source.readString();
- vr.mUserIds = new ArrayList<String>();
+ vr.mUserIds = new ArrayList<>();
source.readStringList(vr.mUserIds);
vr.mMergedUserIds = (HashMap<String, HashSet<String>>) source.readSerializable();
vr.mKeyId = source.readLong();
@@ -103,7 +103,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.mSecretKey = source.readByte() == 1;
vr.mSelected = source.readByte() == 1;
vr.mExtraData = source.readString();
- vr.mOrigins = new ArrayList<String>();
+ vr.mOrigins = new ArrayList<>();
source.readStringList(vr.mOrigins);
return vr;
@@ -265,8 +265,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
mSecretKey = false;
// do not select by default
mSelected = false;
- mUserIds = new ArrayList<String>();
- mOrigins = new ArrayList<String>();
+ mUserIds = new ArrayList<>();
+ mOrigins = new ArrayList<>();
}
/**
@@ -294,8 +294,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
mKeyId = key.getKeyId();
mKeyIdHex = KeyFormattingUtils.convertKeyIdToHex(mKeyId);
- mRevoked = key.isRevoked();
- mExpired = key.isExpired();
+ mRevoked = key.isMaybeRevoked();
+ mExpired = key.isMaybeExpired();
mFingerprintHex = KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint());
mBitStrength = key.getBitStrength();
mCurveOid = key.getCurveOid();
@@ -304,7 +304,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
}
public void updateMergedUserIds() {
- mMergedUserIds = new HashMap<String, HashSet<String>>();
+ mMergedUserIds = new HashMap<>();
for (String userId : mUserIds) {
String[] userIdSplit = KeyRing.splitUserId(userId);
@@ -315,7 +315,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
// email
if (userIdSplit[1] != null) {
if (!mMergedUserIds.containsKey(userIdSplit[0])) {
- HashSet<String> emails = new HashSet<String>();
+ HashSet<String> emails = new HashSet<>();
emails.add(userIdSplit[1]);
mMergedUserIds.put(userIdSplit[0], emails);
} else {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
index 2363a40b3..e310e9a3f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
@@ -36,7 +36,7 @@ public class KeybaseKeyserver extends Keyserver {
@Override
public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException,
QueryNeedsRepairException {
- ArrayList<ImportKeysListEntry> results = new ArrayList<ImportKeysListEntry>();
+ ArrayList<ImportKeysListEntry> results = new ArrayList<>();
if (query.startsWith("0x")) {
// cut off "0x" if a user is searching for a key id
@@ -81,7 +81,7 @@ public class KeybaseKeyserver extends Keyserver {
final int algorithmId = match.getAlgorithmId();
entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithmId, bitStrength, null));
- ArrayList<String> userIds = new ArrayList<String>();
+ ArrayList<String> userIds = new ArrayList<>();
String name = "<keybase.io/" + username + ">";
if (fullName != null) {
name = fullName + " " + name;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java
index c400eb813..e796bdc91 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java
@@ -77,6 +77,12 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
return mCancelled != null && mCancelled.get();
}
+ protected void setPreventCancel () {
+ if (mProgressable != null) {
+ mProgressable.setPreventCancel();
+ }
+ }
+
@Override
public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException {
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
index a5eb95b07..025f45f7f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
@@ -5,6 +5,10 @@ import android.content.Context;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
+import org.sufficientlysecure.keychain.operations.results.CertifyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
@@ -16,10 +20,6 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
@@ -78,7 +78,7 @@ public class CertifyOperation extends BaseOperation {
return new CertifyResult(CertifyResult.RESULT_ERROR, log);
}
- ArrayList<UncachedKeyRing> certifiedKeys = new ArrayList<UncachedKeyRing>();
+ ArrayList<UncachedKeyRing> certifiedKeys = new ArrayList<>();
log.add(LogType.MSG_CRT_CERTIFYING, 1);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
index 4d466593b..7f14b08d9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
@@ -4,17 +4,16 @@ import android.content.Context;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
-import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
@@ -29,7 +28,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* create key operations in PgpKeyOperation. It takes care of fetching
* and saving the key before and after the operation.
*
- * @see CertifyActionsParcel
+ * @see SaveKeyringParcel
*
*/
public class EditKeyOperation extends BaseOperation {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java
index 6ca28142f..8f10377cd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java
@@ -30,7 +30,12 @@ import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
@@ -40,13 +45,12 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableFileCache;
+import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedOutputStream;
@@ -58,6 +62,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/** An operation class which implements high level import and export
@@ -123,6 +128,35 @@ public class ImportExportOperation extends BaseOperation {
}
}
+ public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries, String keyServerUri) {
+
+ Iterator<ParcelableKeyRing> it = entries.iterator();
+ int numEntries = entries.size();
+
+ return importKeyRings(it, numEntries, keyServerUri);
+
+ }
+
+ public ImportKeyResult importKeyRings(ParcelableFileCache<ParcelableKeyRing> cache, String keyServerUri) {
+
+ // get entries from cached file
+ try {
+ IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
+ int numEntries = it.getSize();
+
+ return importKeyRings(it, numEntries, keyServerUri);
+ } catch (IOException e) {
+
+ // Special treatment here, we need a lot
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_IMPORT, 0, 0);
+ log.add(LogType.MSG_IMPORT_ERROR_IO, 0, 0);
+
+ return new ImportKeyResult(ImportKeyResult.RESULT_ERROR, log);
+ }
+
+ }
+
public ImportKeyResult importKeyRings(Iterator<ParcelableKeyRing> entries, int num, String keyServerUri) {
updateProgress(R.string.progress_importing, 0, 100);
@@ -131,13 +165,11 @@ public class ImportExportOperation extends BaseOperation {
// If there aren't even any keys, do nothing here.
if (entries == null || !entries.hasNext()) {
- return new ImportKeyResult(
- ImportKeyResult.RESULT_FAIL_NOTHING, log, 0, 0, 0, 0,
- new long[]{});
+ return new ImportKeyResult(ImportKeyResult.RESULT_FAIL_NOTHING, log);
}
int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0;
- ArrayList<Long> importedMasterKeyIds = new ArrayList<Long>();
+ ArrayList<Long> importedMasterKeyIds = new ArrayList<>();
boolean cancelled = false;
int position = 0;
@@ -293,6 +325,16 @@ public class ImportExportOperation extends BaseOperation {
position++;
}
+ // Special: consolidate on secret key import (cannot be cancelled!)
+ if (secret > 0) {
+ setPreventCancel();
+ ConsolidateResult result = mProviderHelper.consolidateDatabaseStep1(mProgressable);
+ log.add(result, 1);
+ }
+
+ // Special: make sure new data is synced into contacts
+ ContactSyncAdapterService.requestSync();
+
// convert to long array
long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()];
for (int i = 0; i < importedMasterKeyIds.size(); ++i) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
new file mode 100644
index 000000000..783b32477
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
@@ -0,0 +1,103 @@
+package org.sufficientlysecure.keychain.operations;
+
+import android.content.Context;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
+import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** An operation which promotes a public key ring to a secret one.
+ *
+ * This operation can only be applied to public key rings where no secret key
+ * is available. Doing this "promotes" the public key ring to a secret one
+ * without secret key material, using a GNU_DUMMY s2k type.
+ *
+ */
+public class PromoteKeyOperation extends BaseOperation {
+
+ public PromoteKeyOperation(Context context, ProviderHelper providerHelper,
+ Progressable progressable, AtomicBoolean cancelled) {
+ super(context, providerHelper, progressable, cancelled);
+ }
+
+ public PromoteKeyResult execute(long masterKeyId) {
+
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_PR, 0);
+
+ // Perform actual type change
+ UncachedKeyRing promotedRing;
+ {
+
+ try {
+
+ // This operation is only allowed for pure public keys
+ // TODO delete secret keys if they are stripped, or have been moved to the card?
+ if (mProviderHelper.getCachedPublicKeyRing(masterKeyId).hasAnySecret()) {
+ log.add(LogType.MSG_PR_ERROR_ALREADY_SECRET, 2);
+ return new PromoteKeyResult(PromoteKeyResult.RESULT_ERROR, log, null);
+ }
+
+ log.add(LogType.MSG_PR_FETCHING, 1,
+ KeyFormattingUtils.convertKeyIdToHex(masterKeyId));
+ CanonicalizedPublicKeyRing pubRing =
+ mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId);
+
+ // create divert-to-card secret key from public key
+ promotedRing = pubRing.createDummySecretRing();
+
+ } catch (PgpKeyNotFoundException e) {
+ log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2);
+ return new PromoteKeyResult(PromoteKeyResult.RESULT_ERROR, log, null);
+ } catch (NotFoundException e) {
+ log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2);
+ return new PromoteKeyResult(PromoteKeyResult.RESULT_ERROR, log, null);
+ }
+ }
+
+ // If the edit operation didn't succeed, exit here
+ if (promotedRing == null) {
+ // error is already logged by modification
+ return new PromoteKeyResult(PromoteKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // Check if the action was cancelled
+ if (checkCancelled()) {
+ log.add(LogType.MSG_OPERATION_CANCELLED, 0);
+ return new PromoteKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
+ }
+
+ // Cannot cancel from here on out!
+ setPreventCancel();
+
+ // Save the new keyring.
+ SaveKeyringResult saveResult = mProviderHelper
+ .saveSecretKeyRing(promotedRing, new ProgressScaler(mProgressable, 60, 95, 100));
+ log.add(saveResult, 1);
+
+ // If the save operation didn't succeed, exit here
+ if (!saveResult.success()) {
+ return new PromoteKeyResult(PromoteKeyResult.RESULT_ERROR, log, null);
+ }
+
+ updateProgress(R.string.progress_done, 100, 100);
+
+ log.add(LogType.MSG_PR_SUCCESS, 0);
+ return new PromoteKeyResult(PromoteKeyResult.RESULT_OK, log, promotedRing.getMasterKeyId());
+
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
new file mode 100644
index 000000000..e98f6b150
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
@@ -0,0 +1,136 @@
+package org.sufficientlysecure.keychain.operations;
+
+import android.content.Context;
+import android.net.Uri;
+
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
+import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
+import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.util.FileHelper;
+import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** This is a high-level operation, which encapsulates one or more sign/encrypt
+ * operations, using URIs or byte arrays as input and output.
+ *
+ * This operation is fail-fast: If any sign/encrypt sub-operation fails or returns
+ * a pending result, it will terminate.
+ *
+ */
+public class SignEncryptOperation extends BaseOperation {
+
+ public SignEncryptOperation(Context context, ProviderHelper providerHelper,
+ Progressable progressable, AtomicBoolean cancelled) {
+ super(context, providerHelper, progressable, cancelled);
+ }
+
+ public SignEncryptResult execute(SignEncryptParcel input) {
+
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_SE, 0);
+
+ ArrayDeque<Uri> inputUris = new ArrayDeque<>(input.getInputUris());
+ ArrayDeque<Uri> outputUris = new ArrayDeque<>(input.getOutputUris());
+ byte[] inputBytes = input.getBytes();
+ byte[] outputBytes = null;
+
+ ArrayList<PgpSignEncryptResult> results = new ArrayList<>();
+
+ do {
+
+ if (checkCancelled()) {
+ log.add(LogType.MSG_OPERATION_CANCELLED, 0);
+ return new SignEncryptResult(SignEncryptResult.RESULT_CANCELLED, log, results);
+ }
+
+ InputData inputData;
+ {
+ if (inputBytes != null) {
+ log.add(LogType.MSG_SE_INPUT_BYTES, 1);
+ InputStream is = new ByteArrayInputStream(inputBytes);
+ inputData = new InputData(is, inputBytes.length);
+ inputBytes = null;
+ } else {
+ if (inputUris.isEmpty()) {
+ log.add(LogType.MSG_SE_ERROR_NO_INPUT, 1);
+ return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log, results);
+ }
+
+ log.add(LogType.MSG_SE_INPUT_URI, 1);
+ Uri uri = inputUris.removeFirst();
+ try {
+ InputStream is = mContext.getContentResolver().openInputStream(uri);
+ long fileSize = FileHelper.getFileSize(mContext, uri, 0);
+ String filename = FileHelper.getFilename(mContext, uri);
+ inputData = new InputData(is, fileSize, filename);
+ } catch (FileNotFoundException e) {
+ log.add(LogType.MSG_SE_ERROR_INPUT_URI_NOT_FOUND, 1);
+ return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log, results);
+ }
+ }
+ }
+
+ OutputStream outStream;
+ {
+ if (!outputUris.isEmpty()) {
+ try {
+ Uri outputUri = outputUris.removeFirst();
+ outStream = mContext.getContentResolver().openOutputStream(outputUri);
+ } catch (FileNotFoundException e) {
+ log.add(LogType.MSG_SE_ERROR_OUTPUT_URI_NOT_FOUND, 1);
+ return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log, results);
+ }
+ } else {
+ if (outputBytes != null) {
+ log.add(LogType.MSG_SE_ERROR_TOO_MANY_INPUTS, 1);
+ return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log, results);
+ }
+ outStream = new ByteArrayOutputStream();
+ }
+ }
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(mContext, mProviderHelper,
+ new ProgressScaler(), mCancelled);
+ PgpSignEncryptResult result = op.execute(input, inputData, outStream);
+ results.add(result);
+ log.add(result, 2);
+
+ if (result.isPending()) {
+ return new SignEncryptResult(SignEncryptResult.RESULT_PENDING, log, results);
+ }
+
+ if (!result.success()) {
+ return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log, results);
+ }
+
+ if (outStream instanceof ByteArrayOutputStream) {
+ outputBytes = ((ByteArrayOutputStream) outStream).toByteArray();
+ }
+
+ } while (!inputUris.isEmpty());
+
+ if (!outputUris.isEmpty()) {
+ // Any output URIs left are indicative of a programming error
+ log.add(LogType.MSG_SE_WARN_OUTPUT_LEFT, 1);
+ }
+
+ log.add(LogType.MSG_SE_SUCCESS, 1);
+ return new SignEncryptResult(SignEncryptResult.RESULT_OK, log, results, outputBytes);
+
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java
index 7b38cd244..94684851a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java
@@ -21,18 +21,14 @@ package org.sufficientlysecure.keychain.operations.results;
import android.app.Activity;
import android.content.Intent;
import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.View;
-
-import com.github.johnpersano.supertoasts.SuperCardToast;
-import com.github.johnpersano.supertoasts.SuperToast;
-import com.github.johnpersano.supertoasts.SuperToast.Duration;
-import com.github.johnpersano.supertoasts.util.OnClickWrapper;
-import com.github.johnpersano.supertoasts.util.Style;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
+import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
public class CertifyResult extends OperationResult {
@@ -78,30 +74,31 @@ public class CertifyResult extends OperationResult {
}
};
- public SuperCardToast createNotify(final Activity activity) {
+ public Showable createNotify(final Activity activity) {
int resultType = getResult();
String str;
- int duration, color;
+ int duration;
+ Style style;
// Not an overall failure
if ((resultType & OperationResult.RESULT_ERROR) == 0) {
String withWarnings;
- duration = Duration.EXTRA_LONG;
- color = Style.GREEN;
+ duration = Notify.LENGTH_LONG;
+ style = Style.OK;
withWarnings = "";
// Any warnings?
if ((resultType & ImportKeyResult.RESULT_WARNINGS) > 0) {
duration = 0;
- color = Style.ORANGE;
+ style = Style.WARN;
withWarnings += activity.getString(R.string.with_warnings);
}
if ((resultType & ImportKeyResult.RESULT_CANCELLED) > 0) {
duration = 0;
- color = Style.ORANGE;
+ style = Style.WARN;
withWarnings += activity.getString(R.string.with_cancelled);
}
@@ -111,46 +108,27 @@ public class CertifyResult extends OperationResult {
if (mCertifyError > 0) {
// definitely switch to warning-style message in this case!
duration = 0;
- color = Style.RED;
+ style = Style.ERROR;
str += " " + activity.getResources().getQuantityString(
R.plurals.certify_keys_with_errors, mCertifyError, mCertifyError);
}
} else {
duration = 0;
- color = Style.RED;
+ style = Style.ERROR;
str = activity.getResources().getQuantityString(R.plurals.certify_error,
mCertifyError, mCertifyError);
}
- boolean button = getLog() != null && !getLog().isEmpty();
- SuperCardToast toast = new SuperCardToast(activity,
- button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD,
- Style.getStyle(color, SuperToast.Animations.POPUP));
- toast.setText(str);
- toast.setDuration(duration);
- toast.setIndeterminate(duration == 0);
- toast.setSwipeToDismiss(true);
- // If we have a log and it's non-empty, show a View Log button
- if (button) {
- toast.setButtonIcon(R.drawable.ic_action_view_as_list,
- activity.getResources().getString(R.string.view_log));
- toast.setButtonTextColor(activity.getResources().getColor(R.color.black));
- toast.setTextColor(activity.getResources().getColor(R.color.black));
- toast.setOnClickWrapper(new OnClickWrapper("supercardtoast",
- new SuperToast.OnClickListener() {
- @Override
- public void onClick(View view, Parcelable token) {
- Intent intent = new Intent(
- activity, LogDisplayActivity.class);
- intent.putExtra(LogDisplayFragment.EXTRA_RESULT, CertifyResult.this);
- activity.startActivity(intent);
- }
- }
- ));
- }
-
- return toast;
+ return Notify.createNotify(activity, str, duration, style, new ActionListener() {
+ @Override
+ public void onAction() {
+ Intent intent = new Intent(
+ activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, CertifyResult.this);
+ activity.startActivity(intent);
+ }
+ }, R.string.view_log);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
index d81577102..86b37fea6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
@@ -41,6 +41,9 @@ public class DecryptVerifyResult extends OperationResult {
OpenPgpSignatureResult mSignatureResult;
OpenPgpMetadata mDecryptMetadata;
+ // This holds the charset which was specified in the ascii armor, if specified
+ // https://tools.ietf.org/html/rfc4880#page56
+ String mCharset;
public long getKeyIdPassphraseNeeded() {
return mKeyIdPassphraseNeeded;
@@ -84,6 +87,14 @@ public class DecryptVerifyResult extends OperationResult {
mDecryptMetadata = decryptMetadata;
}
+ public String getCharset () {
+ return mCharset;
+ }
+
+ public void setCharset(String charset) {
+ mCharset = charset;
+ }
+
public boolean isPending() {
return (mResult & RESULT_PENDING) == RESULT_PENDING;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java
index 1ca5ad20a..62197541a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java
@@ -21,18 +21,14 @@ package org.sufficientlysecure.keychain.operations.results;
import android.app.Activity;
import android.content.Intent;
import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.View;
-
-import com.github.johnpersano.supertoasts.SuperCardToast;
-import com.github.johnpersano.supertoasts.SuperToast;
-import com.github.johnpersano.supertoasts.SuperToast.Duration;
-import com.github.johnpersano.supertoasts.util.OnClickWrapper;
-import com.github.johnpersano.supertoasts.util.Style;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
+import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
public class DeleteResult extends OperationResult {
@@ -68,31 +64,32 @@ public class DeleteResult extends OperationResult {
}
};
- public SuperCardToast createNotify(final Activity activity) {
+ public Showable createNotify(final Activity activity) {
int resultType = getResult();
String str;
- int duration, color;
+ int duration;
+ Style style;
// Not an overall failure
if ((resultType & OperationResult.RESULT_ERROR) == 0) {
String untilCancelled;
- duration = Duration.EXTRA_LONG;
- color = Style.GREEN;
+ duration = Notify.LENGTH_LONG;
+ style = Style.OK;
untilCancelled = "";
// Any warnings?
if ((resultType & ImportKeyResult.RESULT_CANCELLED) > 0) {
duration = 0;
- color = Style.ORANGE;
+ style = Style.WARN;
untilCancelled += activity.getString(R.string.with_cancelled);
}
// New and updated keys
if (mOk > 0 && mFail > 0) {
- color = Style.ORANGE;
+ style = Style.WARN;
duration = 0;
str = activity.getResources().getQuantityString(
R.plurals.delete_ok_but_fail_1, mOk, mOk);
@@ -105,13 +102,13 @@ public class DeleteResult extends OperationResult {
str = activity.getString(R.string.delete_cancelled);
} else {
duration = 0;
- color = Style.RED;
+ style = Style.ERROR;
str = "internal error";
}
} else {
duration = 0;
- color = Style.RED;
+ style = Style.ERROR;
if (mFail == 0) {
str = activity.getString(R.string.delete_nothing);
} else {
@@ -119,34 +116,15 @@ public class DeleteResult extends OperationResult {
}
}
- boolean button = getLog() != null && !getLog().isEmpty();
- SuperCardToast toast = new SuperCardToast(activity,
- button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD,
- Style.getStyle(color, SuperToast.Animations.POPUP));
- toast.setText(str);
- toast.setDuration(duration);
- toast.setIndeterminate(duration == 0);
- toast.setSwipeToDismiss(true);
- // If we have a log and it's non-empty, show a View Log button
- if (button) {
- toast.setButtonIcon(R.drawable.ic_action_view_as_list,
- activity.getResources().getString(R.string.view_log));
- toast.setButtonTextColor(activity.getResources().getColor(R.color.black));
- toast.setTextColor(activity.getResources().getColor(R.color.black));
- toast.setOnClickWrapper(new OnClickWrapper("supercardtoast",
- new SuperToast.OnClickListener() {
- @Override
- public void onClick(View view, Parcelable token) {
- Intent intent = new Intent(
- activity, LogDisplayActivity.class);
- intent.putExtra(LogDisplayFragment.EXTRA_RESULT, DeleteResult.this);
- activity.startActivity(intent);
- }
- }
- ));
- }
-
- return toast;
+ return Notify.createNotify(activity, str, duration, style, new ActionListener() {
+ @Override
+ public void onAction() {
+ Intent intent = new Intent(
+ activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, DeleteResult.this);
+ activity.startActivity(intent);
+ }
+ }, R.string.view_log);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java
index 1dd038a23..2d533ed16 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java
@@ -21,18 +21,14 @@ package org.sufficientlysecure.keychain.operations.results;
import android.app.Activity;
import android.content.Intent;
import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.View;
-
-import com.github.johnpersano.supertoasts.SuperCardToast;
-import com.github.johnpersano.supertoasts.SuperToast;
-import com.github.johnpersano.supertoasts.SuperToast.Duration;
-import com.github.johnpersano.supertoasts.util.OnClickWrapper;
-import com.github.johnpersano.supertoasts.util.Style;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
+import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
public class ImportKeyResult extends OperationResult {
@@ -83,6 +79,10 @@ public class ImportKeyResult extends OperationResult {
mImportedMasterKeyIds = source.createLongArray();
}
+ public ImportKeyResult(int result, OperationLog log) {
+ this(result, log, 0, 0, 0, 0, new long[] { });
+ }
+
public ImportKeyResult(int result, OperationLog log,
int newKeys, int updatedKeys, int badKeys, int secret,
long[] importedMasterKeyIds) {
@@ -114,30 +114,31 @@ public class ImportKeyResult extends OperationResult {
}
};
- public SuperCardToast createNotify(final Activity activity) {
+ public Showable createNotify(final Activity activity) {
int resultType = getResult();
String str;
- int duration, color;
+ int duration;
+ Style style;
// Not an overall failure
if ((resultType & OperationResult.RESULT_ERROR) == 0) {
String withWarnings;
- duration = Duration.EXTRA_LONG;
- color = Style.GREEN;
+ duration = Notify.LENGTH_LONG;
+ style = Style.OK;
withWarnings = "";
// Any warnings?
if ((resultType & ImportKeyResult.RESULT_WARNINGS) > 0) {
duration = 0;
- color = Style.ORANGE;
+ style = Style.WARN;
withWarnings += activity.getString(R.string.with_warnings);
}
if ((resultType & ImportKeyResult.RESULT_CANCELLED) > 0) {
duration = 0;
- color = Style.ORANGE;
+ style = Style.WARN;
withWarnings += activity.getString(R.string.with_cancelled);
}
@@ -155,20 +156,20 @@ public class ImportKeyResult extends OperationResult {
R.plurals.import_keys_added, mNewKeys, mNewKeys, withWarnings);
} else {
duration = 0;
- color = Style.RED;
+ style = Style.ERROR;
str = "internal error";
}
if (isOkWithErrors()) {
// definitely switch to warning-style message in this case!
duration = 0;
- color = Style.RED;
+ style = Style.ERROR;
str += " " + activity.getResources().getQuantityString(
R.plurals.import_keys_with_errors, mBadKeys, mBadKeys);
}
} else {
duration = 0;
- color = Style.RED;
+ style = Style.ERROR;
if (isFailNothing()) {
str = activity.getString((resultType & ImportKeyResult.RESULT_CANCELLED) > 0
? R.string.import_error_nothing_cancelled
@@ -178,34 +179,15 @@ public class ImportKeyResult extends OperationResult {
}
}
- boolean button = getLog() != null && !getLog().isEmpty();
- SuperCardToast toast = new SuperCardToast(activity,
- button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD,
- Style.getStyle(color, SuperToast.Animations.POPUP));
- toast.setText(str);
- toast.setDuration(duration);
- toast.setIndeterminate(duration == 0);
- toast.setSwipeToDismiss(true);
- // If we have a log and it's non-empty, show a View Log button
- if (button) {
- toast.setButtonIcon(R.drawable.ic_action_view_as_list,
- activity.getResources().getString(R.string.view_log));
- toast.setButtonTextColor(activity.getResources().getColor(R.color.black));
- toast.setTextColor(activity.getResources().getColor(R.color.black));
- toast.setOnClickWrapper(new OnClickWrapper("supercardtoast",
- new SuperToast.OnClickListener() {
- @Override
- public void onClick(View view, Parcelable token) {
- Intent intent = new Intent(
- activity, LogDisplayActivity.class);
- intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ImportKeyResult.this);
- activity.startActivity(intent);
- }
- }
- ));
- }
-
- return toast;
+ return Notify.createNotify(activity, str, duration, style, new ActionListener() {
+ @Override
+ public void onAction() {
+ Intent intent = new Intent(
+ activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ImportKeyResult.this);
+ activity.startActivity(intent);
+ }
+ }, R.string.view_log);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index af401b05a..ea4fedc56 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -22,17 +22,15 @@ import android.app.Activity;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
-import android.view.View;
-
-import com.github.johnpersano.supertoasts.SuperCardToast;
-import com.github.johnpersano.supertoasts.SuperToast;
-import com.github.johnpersano.supertoasts.util.OnClickWrapper;
-import com.github.johnpersano.supertoasts.util.Style;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
+import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -195,58 +193,44 @@ public abstract class OperationResult implements Parcelable {
}
- public SuperCardToast createNotify(final Activity activity) {
-
- int color;
+ public Showable createNotify(final Activity activity) {
Log.d(Constants.TAG, "mLog.getLast()"+mLog.getLast());
Log.d(Constants.TAG, "mLog.getLast().mType"+mLog.getLast().mType);
Log.d(Constants.TAG, "mLog.getLast().mType.getMsgId()"+mLog.getLast().mType.getMsgId());
// Take the last message as string
- String str = activity.getString(mLog.getLast().mType.getMsgId());
+ int msgId = mLog.getLast().mType.getMsgId();
+
+ Style style;
// Not an overall failure
if (cancelled()) {
- color = Style.RED;
+ style = Style.ERROR;
} else if (success()) {
if (getLog().containsWarnings()) {
- color = Style.ORANGE;
+ style = Style.WARN;
} else {
- color = Style.GREEN;
+ style = Style.OK;
}
} else {
- color = Style.RED;
+ style = Style.ERROR;
}
- boolean button = getLog() != null && !getLog().isEmpty();
- SuperCardToast toast = new SuperCardToast(activity,
- button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD,
- Style.getStyle(color, SuperToast.Animations.POPUP));
- toast.setText(str);
- toast.setDuration(SuperToast.Duration.EXTRA_LONG);
- toast.setIndeterminate(false);
- toast.setSwipeToDismiss(true);
- // If we have a log and it's non-empty, show a View Log button
- if (button) {
- toast.setButtonIcon(R.drawable.ic_action_view_as_list,
- activity.getResources().getString(R.string.view_log));
- toast.setButtonTextColor(activity.getResources().getColor(R.color.black));
- toast.setTextColor(activity.getResources().getColor(R.color.black));
- toast.setOnClickWrapper(new OnClickWrapper("supercardtoast",
- new SuperToast.OnClickListener() {
- @Override
- public void onClick(View view, Parcelable token) {
- Intent intent = new Intent(
- activity, LogDisplayActivity.class);
- intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this);
- activity.startActivity(intent);
- }
- }
- ));
+ if (getLog() == null || getLog().isEmpty()) {
+ return Notify.createNotify(activity, msgId, Notify.LENGTH_LONG, style);
}
- return toast;
+ return Notify.createNotify(activity, msgId, Notify.LENGTH_LONG, style,
+ new ActionListener() {
+ @Override
+ public void onAction() {
+ Intent intent = new Intent(
+ activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this);
+ activity.startActivity(intent);
+ }
+ }, R.string.view_log);
}
@@ -473,6 +457,7 @@ public abstract class OperationResult implements Parcelable {
// secret key modify
MSG_MF (LogLevel.START, R.string.msg_mr),
+ MSG_MF_ERROR_DIVERT_SERIAL (LogLevel.ERROR, R.string.msg_mf_error_divert_serial),
MSG_MF_ERROR_ENCODE (LogLevel.ERROR, R.string.msg_mf_error_encode),
MSG_MF_ERROR_FINGERPRINT (LogLevel.ERROR, R.string.msg_mf_error_fingerprint),
MSG_MF_ERROR_KEYID (LogLevel.ERROR, R.string.msg_mf_error_keyid),
@@ -485,6 +470,7 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_ERROR_PASSPHRASE_MASTER(LogLevel.ERROR, R.string.msg_mf_error_passphrase_master),
MSG_MF_ERROR_PAST_EXPIRY(LogLevel.ERROR, R.string.msg_mf_error_past_expiry),
MSG_MF_ERROR_PGP (LogLevel.ERROR, R.string.msg_mf_error_pgp),
+ MSG_MF_ERROR_RESTRICTED(LogLevel.ERROR, R.string.msg_mf_error_restricted),
MSG_MF_ERROR_REVOKED_PRIMARY (LogLevel.ERROR, R.string.msg_mf_error_revoked_primary),
MSG_MF_ERROR_SIG (LogLevel.ERROR, R.string.msg_mf_error_sig),
MSG_MF_ERROR_SUBKEY_MISSING(LogLevel.ERROR, R.string.msg_mf_error_subkey_missing),
@@ -527,6 +513,7 @@ public abstract class OperationResult implements Parcelable {
MSG_CON_ERROR_PUBLIC (LogLevel.ERROR, R.string.msg_con_error_public),
MSG_CON_ERROR_SECRET (LogLevel.ERROR, R.string.msg_con_error_secret),
MSG_CON_RECOVER (LogLevel.DEBUG, R.string.msg_con_recover),
+ MSG_CON_RECURSIVE (LogLevel.OK, R.string.msg_con_recursive),
MSG_CON_REIMPORT_PUBLIC (LogLevel.DEBUG, R.plurals.msg_con_reimport_public),
MSG_CON_REIMPORT_PUBLIC_SKIP (LogLevel.DEBUG, R.string.msg_con_reimport_public_skip),
MSG_CON_REIMPORT_SECRET (LogLevel.DEBUG, R.plurals.msg_con_reimport_secret),
@@ -546,6 +533,13 @@ public abstract class OperationResult implements Parcelable {
MSG_ED_FETCHING (LogLevel.DEBUG, R.string.msg_ed_fetching),
MSG_ED_SUCCESS (LogLevel.OK, R.string.msg_ed_success),
+ // promote key
+ MSG_PR (LogLevel.START, R.string.msg_pr),
+ MSG_PR_ERROR_ALREADY_SECRET (LogLevel.ERROR, R.string.msg_pr_error_already_secret),
+ MSG_PR_ERROR_KEY_NOT_FOUND (LogLevel.ERROR, R.string.msg_pr_error_key_not_found),
+ MSG_PR_FETCHING (LogLevel.DEBUG, R.string.msg_pr_fetching),
+ MSG_PR_SUCCESS (LogLevel.OK, R.string.msg_pr_success),
+
// messages used in UI code
MSG_EK_ERROR_DIVERT (LogLevel.ERROR, R.string.msg_ek_error_divert),
MSG_EK_ERROR_DUMMY (LogLevel.ERROR, R.string.msg_ek_error_dummy),
@@ -555,11 +549,13 @@ public abstract class OperationResult implements Parcelable {
MSG_DC_ASKIP_NO_KEY (LogLevel.DEBUG, R.string.msg_dc_askip_no_key),
MSG_DC_ASKIP_NOT_ALLOWED (LogLevel.DEBUG, R.string.msg_dc_askip_not_allowed),
MSG_DC_ASYM (LogLevel.DEBUG, R.string.msg_dc_asym),
+ MSG_DC_CHARSET (LogLevel.DEBUG, R.string.msg_dc_charset),
MSG_DC_CLEAR_DATA (LogLevel.DEBUG, R.string.msg_dc_clear_data),
MSG_DC_CLEAR_DECOMPRESS (LogLevel.DEBUG, R.string.msg_dc_clear_decompress),
MSG_DC_CLEAR_META_FILE (LogLevel.DEBUG, R.string.msg_dc_clear_meta_file),
MSG_DC_CLEAR_META_MIME (LogLevel.DEBUG, R.string.msg_dc_clear_meta_mime),
MSG_DC_CLEAR_META_SIZE (LogLevel.DEBUG, R.string.msg_dc_clear_meta_size),
+ MSG_DC_CLEAR_META_SIZE_UNKNOWN (LogLevel.DEBUG, R.string.msg_dc_clear_meta_size_unknown),
MSG_DC_CLEAR_META_TIME (LogLevel.DEBUG, R.string.msg_dc_clear_meta_time),
MSG_DC_CLEAR (LogLevel.DEBUG, R.string.msg_dc_clear),
MSG_DC_CLEAR_SIGNATURE_BAD (LogLevel.WARN, R.string.msg_dc_clear_signature_bad),
@@ -590,29 +586,52 @@ public abstract class OperationResult implements Parcelable {
MSG_DC_TRAIL_UNKNOWN (LogLevel.DEBUG, R.string.msg_dc_trail_unknown),
MSG_DC_UNLOCKING (LogLevel.INFO, R.string.msg_dc_unlocking),
+ // verify signed literal data
+ MSG_VL (LogLevel.INFO, R.string.msg_vl),
+ MSG_VL_ERROR_MISSING_SIGLIST (LogLevel.ERROR, R.string.msg_vl_error_no_siglist),
+ MSG_VL_ERROR_MISSING_LITERAL (LogLevel.ERROR, R.string.msg_vl_error_missing_literal),
+ MSG_VL_ERROR_MISSING_KEY (LogLevel.ERROR, R.string.msg_vl_error_wrong_key),
+ MSG_VL_CLEAR_SIGNATURE_CHECK (LogLevel.DEBUG, R.string.msg_vl_clear_signature_check),
+ MSG_VL_ERROR_INTEGRITY_CHECK (LogLevel.ERROR, R.string.msg_vl_error_integrity_check),
+ MSG_VL_OK (LogLevel.OK, R.string.msg_vl_ok),
+
// signencrypt
- MSG_SE_ASYMMETRIC (LogLevel.INFO, R.string.msg_se_asymmetric),
- MSG_SE_CLEARSIGN_ONLY (LogLevel.DEBUG, R.string.msg_se_clearsign_only),
- MSG_SE_COMPRESSING (LogLevel.DEBUG, R.string.msg_se_compressing),
- MSG_SE_ENCRYPTING (LogLevel.DEBUG, R.string.msg_se_encrypting),
- MSG_SE_ERROR_BAD_PASSPHRASE (LogLevel.ERROR, R.string.msg_se_error_bad_passphrase),
- MSG_SE_ERROR_IO (LogLevel.ERROR, R.string.msg_se_error_io),
- MSG_SE_ERROR_SIGN_KEY(LogLevel.ERROR, R.string.msg_se_error_sign_key),
- MSG_SE_ERROR_KEY_SIGN (LogLevel.ERROR, R.string.msg_se_error_key_sign),
- MSG_SE_ERROR_NFC (LogLevel.ERROR, R.string.msg_se_error_nfc),
- MSG_SE_ERROR_PGP (LogLevel.ERROR, R.string.msg_se_error_pgp),
- MSG_SE_ERROR_SIG (LogLevel.ERROR, R.string.msg_se_error_sig),
- MSG_SE_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_se_error_unlock),
- MSG_SE_KEY_OK (LogLevel.OK, R.string.msg_se_key_ok),
- MSG_SE_KEY_UNKNOWN (LogLevel.DEBUG, R.string.msg_se_key_unknown),
- MSG_SE_KEY_WARN (LogLevel.WARN, R.string.msg_se_key_warn),
- MSG_SE_OK (LogLevel.OK, R.string.msg_se_ok),
- MSG_SE_PENDING_NFC (LogLevel.INFO, R.string.msg_se_pending_nfc),
- MSG_SE_PENDING_PASSPHRASE (LogLevel.INFO, R.string.msg_se_pending_passphrase),
- MSG_SE (LogLevel.DEBUG, R.string.msg_se),
- MSG_SE_SIGNING (LogLevel.DEBUG, R.string.msg_se_signing),
- MSG_SE_SIGCRYPTING (LogLevel.DEBUG, R.string.msg_se_sigcrypting),
- MSG_SE_SYMMETRIC (LogLevel.INFO, R.string.msg_se_symmetric),
+ MSG_SE (LogLevel.START, R.string.msg_se),
+ MSG_SE_INPUT_BYTES (LogLevel.INFO, R.string.msg_se_input_bytes),
+ MSG_SE_INPUT_URI (LogLevel.INFO, R.string.msg_se_input_uri),
+ MSG_SE_ERROR_NO_INPUT (LogLevel.DEBUG, R.string.msg_se_error_no_input),
+ MSG_SE_ERROR_INPUT_URI_NOT_FOUND (LogLevel.ERROR, R.string.msg_se_error_input_uri_not_found),
+ MSG_SE_ERROR_OUTPUT_URI_NOT_FOUND (LogLevel.ERROR, R.string.msg_se_error_output_uri_not_found),
+ MSG_SE_ERROR_TOO_MANY_INPUTS (LogLevel.ERROR, R.string.msg_se_error_too_many_inputs),
+ MSG_SE_WARN_OUTPUT_LEFT (LogLevel.WARN, R.string.msg_se_warn_output_left),
+ MSG_SE_SUCCESS (LogLevel.OK, R.string.msg_se_success),
+
+ // pgpsignencrypt
+ MSG_PSE_ASYMMETRIC (LogLevel.INFO, R.string.msg_pse_asymmetric),
+ MSG_PSE_CLEARSIGN_ONLY (LogLevel.DEBUG, R.string.msg_pse_clearsign_only),
+ MSG_PSE_COMPRESSING (LogLevel.DEBUG, R.string.msg_pse_compressing),
+ MSG_PSE_ENCRYPTING (LogLevel.DEBUG, R.string.msg_pse_encrypting),
+ MSG_PSE_ERROR_BAD_PASSPHRASE (LogLevel.ERROR, R.string.msg_pse_error_bad_passphrase),
+ MSG_PSE_ERROR_HASH_ALGO (LogLevel.ERROR, R.string.msg_pse_error_hash_algo),
+ MSG_PSE_ERROR_IO (LogLevel.ERROR, R.string.msg_pse_error_io),
+ MSG_PSE_ERROR_SIGN_KEY(LogLevel.ERROR, R.string.msg_pse_error_sign_key),
+ MSG_PSE_ERROR_KEY_SIGN (LogLevel.ERROR, R.string.msg_pse_error_key_sign),
+ MSG_PSE_ERROR_NFC (LogLevel.ERROR, R.string.msg_pse_error_nfc),
+ MSG_PSE_ERROR_PGP (LogLevel.ERROR, R.string.msg_pse_error_pgp),
+ MSG_PSE_ERROR_SIG (LogLevel.ERROR, R.string.msg_pse_error_sig),
+ MSG_PSE_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_pse_error_unlock),
+ MSG_PSE_KEY_OK (LogLevel.OK, R.string.msg_pse_key_ok),
+ MSG_PSE_KEY_UNKNOWN (LogLevel.DEBUG, R.string.msg_pse_key_unknown),
+ MSG_PSE_KEY_WARN (LogLevel.WARN, R.string.msg_pse_key_warn),
+ MSG_PSE_OK (LogLevel.OK, R.string.msg_pse_ok),
+ MSG_PSE_PENDING_NFC (LogLevel.INFO, R.string.msg_pse_pending_nfc),
+ MSG_PSE_PENDING_PASSPHRASE (LogLevel.INFO, R.string.msg_pse_pending_passphrase),
+ MSG_PSE (LogLevel.DEBUG, R.string.msg_pse),
+ MSG_PSE_SIGNING (LogLevel.DEBUG, R.string.msg_pse_signing),
+ MSG_PSE_SIGNING_CLEARTEXT (LogLevel.DEBUG, R.string.msg_pse_signing_cleartext),
+ MSG_PSE_SIGNING_DETACHED (LogLevel.DEBUG, R.string.msg_pse_signing_detached),
+ MSG_PSE_SIGCRYPTING (LogLevel.DEBUG, R.string.msg_pse_sigcrypting),
+ MSG_PSE_SYMMETRIC (LogLevel.INFO, R.string.msg_pse_symmetric),
MSG_CRT_CERTIFYING (LogLevel.DEBUG, R.string.msg_crt_certifying),
MSG_CRT_CERTIFY_ALL (LogLevel.DEBUG, R.string.msg_crt_certify_all),
@@ -645,6 +664,7 @@ public abstract class OperationResult implements Parcelable {
MSG_IMPORT_FINGERPRINT_ERROR (LogLevel.ERROR, R.string.msg_import_fingerprint_error),
MSG_IMPORT_FINGERPRINT_OK (LogLevel.DEBUG, R.string.msg_import_fingerprint_ok),
MSG_IMPORT_ERROR (LogLevel.ERROR, R.string.msg_import_error),
+ MSG_IMPORT_ERROR_IO (LogLevel.ERROR, R.string.msg_import_error_io),
MSG_IMPORT_PARTIAL (LogLevel.ERROR, R.string.msg_import_partial),
MSG_IMPORT_SUCCESS (LogLevel.OK, R.string.msg_import_success),
@@ -742,7 +762,7 @@ public abstract class OperationResult implements Parcelable {
public static class OperationLog implements Iterable<LogEntryParcel> {
- private final List<LogEntryParcel> mParcels = new ArrayList<LogEntryParcel>();
+ private final List<LogEntryParcel> mParcels = new ArrayList<>();
/// Simple convenience method
public void add(LogType type, int indent, Object... parameters) {
@@ -767,7 +787,7 @@ public abstract class OperationResult implements Parcelable {
}
public boolean containsType(LogType type) {
- for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
+ for(LogEntryParcel entry : new IterableIterator<>(mParcels.iterator())) {
if (entry.mType == type) {
return true;
}
@@ -776,7 +796,7 @@ public abstract class OperationResult implements Parcelable {
}
public boolean containsWarnings() {
- for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
+ for(LogEntryParcel entry : new IterableIterator<>(mParcels.iterator())) {
if (entry.mType.mLevel == LogLevel.WARN || entry.mType.mLevel == LogLevel.ERROR) {
return true;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java
new file mode 100644
index 000000000..de2f64404
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * 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.operations.results;
+
+import android.os.Parcel;
+
+import java.util.Date;
+
+public class PgpSignEncryptResult extends OperationResult {
+
+ // the fourth bit indicates a "data pending" result! (it's also a form of non-success)
+ public static final int RESULT_PENDING = RESULT_ERROR + 8;
+
+ // fifth to sixth bit in addition indicate specific type of pending
+ public static final int RESULT_PENDING_PASSPHRASE = RESULT_PENDING + 16;
+ public static final int RESULT_PENDING_NFC = RESULT_PENDING + 32;
+
+ long mKeyIdPassphraseNeeded;
+
+ long mNfcKeyId;
+ byte[] mNfcHash;
+ int mNfcAlgo;
+ Date mNfcTimestamp;
+ String mNfcPassphrase;
+ byte[] mDetachedSignature;
+
+ public long getKeyIdPassphraseNeeded() {
+ return mKeyIdPassphraseNeeded;
+ }
+
+ public void setKeyIdPassphraseNeeded(long keyIdPassphraseNeeded) {
+ mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
+ }
+
+ public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, String passphrase) {
+ mNfcKeyId = nfcKeyId;
+ mNfcHash = nfcHash;
+ mNfcAlgo = nfcAlgo;
+ mNfcTimestamp = nfcTimestamp;
+ mNfcPassphrase = passphrase;
+ }
+
+ public void setDetachedSignature(byte[] detachedSignature) {
+ mDetachedSignature = detachedSignature;
+ }
+
+ public long getNfcKeyId() {
+ return mNfcKeyId;
+ }
+
+ public byte[] getNfcHash() {
+ return mNfcHash;
+ }
+
+ public int getNfcAlgo() {
+ return mNfcAlgo;
+ }
+
+ public Date getNfcTimestamp() {
+ return mNfcTimestamp;
+ }
+
+ public String getNfcPassphrase() {
+ return mNfcPassphrase;
+ }
+
+ public byte[] getDetachedSignature() {
+ return mDetachedSignature;
+ }
+
+ public boolean isPending() {
+ return (mResult & RESULT_PENDING) == RESULT_PENDING;
+ }
+
+ public PgpSignEncryptResult(int result, OperationLog log) {
+ super(result, log);
+ }
+
+ public PgpSignEncryptResult(Parcel source) {
+ super(source);
+ mNfcHash = source.readInt() != 0 ? source.createByteArray() : null;
+ mNfcAlgo = source.readInt();
+ mNfcTimestamp = source.readInt() != 0 ? new Date(source.readLong()) : null;
+ mDetachedSignature = source.readInt() != 0 ? source.createByteArray() : null;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ if (mNfcHash != null) {
+ dest.writeInt(1);
+ dest.writeByteArray(mNfcHash);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(mNfcAlgo);
+ if (mNfcTimestamp != null) {
+ dest.writeInt(1);
+ dest.writeLong(mNfcTimestamp.getTime());
+ } else {
+ dest.writeInt(0);
+ }
+ if (mDetachedSignature != null) {
+ dest.writeInt(1);
+ dest.writeByteArray(mDetachedSignature);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ public static final Creator<PgpSignEncryptResult> CREATOR = new Creator<PgpSignEncryptResult>() {
+ public PgpSignEncryptResult createFromParcel(final Parcel source) {
+ return new PgpSignEncryptResult(source);
+ }
+
+ public PgpSignEncryptResult[] newArray(final int size) {
+ return new PgpSignEncryptResult[size];
+ }
+ };
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PromoteKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PromoteKeyResult.java
new file mode 100644
index 000000000..af9aff84a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PromoteKeyResult.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * 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.operations.results;
+
+import android.os.Parcel;
+
+public class PromoteKeyResult extends OperationResult {
+
+ public final Long mMasterKeyId;
+
+ public PromoteKeyResult(int result, OperationLog log, Long masterKeyId) {
+ super(result, log);
+ mMasterKeyId = masterKeyId;
+ }
+
+ public PromoteKeyResult(Parcel source) {
+ super(source);
+ mMasterKeyId = source.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeLong(mMasterKeyId);
+ }
+
+ public static Creator<PromoteKeyResult> CREATOR = new Creator<PromoteKeyResult>() {
+ public PromoteKeyResult createFromParcel(final Parcel source) {
+ return new PromoteKeyResult(source);
+ }
+
+ public PromoteKeyResult[] newArray(final int size) {
+ return new PromoteKeyResult[size];
+ }
+ };
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java
index 57daf3430..ed0de65b0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java
@@ -19,74 +19,42 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
-import java.util.Date;
+import java.util.ArrayList;
public class SignEncryptResult extends OperationResult {
- // the fourth bit indicates a "data pending" result! (it's also a form of non-success)
- public static final int RESULT_PENDING = RESULT_ERROR + 8;
-
- // fifth to sixth bit in addition indicate specific type of pending
- public static final int RESULT_PENDING_PASSPHRASE = RESULT_PENDING + 16;
- public static final int RESULT_PENDING_NFC = RESULT_PENDING + 32;
-
- long mKeyIdPassphraseNeeded;
-
- long mNfcKeyId;
- byte[] mNfcHash;
- int mNfcAlgo;
- Date mNfcTimestamp;
- String mNfcPassphrase;
-
- public long getKeyIdPassphraseNeeded() {
- return mKeyIdPassphraseNeeded;
- }
-
- public void setKeyIdPassphraseNeeded(long keyIdPassphraseNeeded) {
- mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
- }
-
- public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, String passphrase) {
- mNfcKeyId = nfcKeyId;
- mNfcHash = nfcHash;
- mNfcAlgo = nfcAlgo;
- mNfcTimestamp = nfcTimestamp;
- mNfcPassphrase = passphrase;
- }
-
- public long getNfcKeyId() {
- return mNfcKeyId;
- }
+ ArrayList<PgpSignEncryptResult> mResults;
+ byte[] mResultBytes;
- public byte[] getNfcHash() {
- return mNfcHash;
- }
-
- public int getNfcAlgo() {
- return mNfcAlgo;
- }
-
- public Date getNfcTimestamp() {
- return mNfcTimestamp;
- }
+ public static final int RESULT_PENDING = RESULT_ERROR + 8;
- public String getNfcPassphrase() {
- return mNfcPassphrase;
+ public PgpSignEncryptResult getPending() {
+ for (PgpSignEncryptResult sub : mResults) {
+ if (sub.isPending()) {
+ return sub;
+ }
+ }
+ return null;
}
- public boolean isPending() {
- return (mResult & RESULT_PENDING) == RESULT_PENDING;
+ public SignEncryptResult(int result, OperationLog log, ArrayList<PgpSignEncryptResult> results) {
+ super(result, log);
+ mResults = results;
}
- public SignEncryptResult(int result, OperationLog log) {
+ public SignEncryptResult(int result, OperationLog log, ArrayList<PgpSignEncryptResult> results, byte[] resultBytes) {
super(result, log);
+ mResults = results;
+ mResultBytes = resultBytes;
}
public SignEncryptResult(Parcel source) {
super(source);
- mNfcHash = source.readInt() != 0 ? source.createByteArray() : null;
- mNfcAlgo = source.readInt();
- mNfcTimestamp = source.readInt() != 0 ? new Date(source.readLong()) : null;
+ mResults = source.createTypedArrayList(PgpSignEncryptResult.CREATOR);
+ }
+
+ public byte[] getResultBytes() {
+ return mResultBytes;
}
public int describeContents() {
@@ -95,19 +63,7 @@ public class SignEncryptResult extends OperationResult {
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- if (mNfcHash != null) {
- dest.writeInt(1);
- dest.writeByteArray(mNfcHash);
- } else {
- dest.writeInt(0);
- }
- dest.writeInt(mNfcAlgo);
- if (mNfcTimestamp != null) {
- dest.writeInt(1);
- dest.writeLong(mNfcTimestamp.getTime());
- } else {
- dest.writeInt(0);
- }
+ dest.writeTypedList(mResults);
}
public static final Creator<SignEncryptResult> CREATOR = new Creator<SignEncryptResult>() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SingletonResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SingletonResult.java
index 43cc85522..04cb241f9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SingletonResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SingletonResult.java
@@ -18,13 +18,8 @@
package org.sufficientlysecure.keychain.operations.results;
-import android.app.Activity;
import android.os.Parcel;
-import com.github.johnpersano.supertoasts.SuperCardToast;
-import com.github.johnpersano.supertoasts.SuperToast;
-import com.github.johnpersano.supertoasts.util.Style;
-
/** This is a simple subclass meant to contain only a single log message. This log
* message is also shown without a log button in the createNotify SuperToast. */
public class SingletonResult extends OperationResult {
@@ -41,35 +36,6 @@ public class SingletonResult extends OperationResult {
}
@Override
- public SuperCardToast createNotify(final Activity activity) {
-
- // there is exactly one error msg - use that one
- String str = activity.getString(mLog.iterator().next().mType.getMsgId());
- int color;
-
- // Determine color by result type
- if (cancelled()) {
- color = Style.RED;
- } else if (success()) {
- if (getLog().containsWarnings()) {
- color = Style.ORANGE;
- } else {
- color = Style.GREEN;
- }
- } else {
- color = Style.RED;
- }
-
- SuperCardToast toast = new SuperCardToast(activity, SuperToast.Type.STANDARD,
- Style.getStyle(color, SuperToast.Animations.POPUP));
- toast.setText(str);
- toast.setDuration(SuperToast.Duration.EXTRA_LONG);
- toast.setIndeterminate(false);
- toast.setSwipeToDismiss(true);
- return toast;
- }
-
- @Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
index db0a9b137..4adacaf23 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
@@ -19,7 +19,6 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPKeyRing;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.util.IterableIterator;
@@ -80,9 +79,8 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
public boolean isExpired() {
// Is the master key expired?
- Date creationDate = getRing().getPublicKey().getCreationTime();
- Date expiryDate = getRing().getPublicKey().getValidSeconds() > 0
- ? new Date(creationDate.getTime() + getRing().getPublicKey().getValidSeconds() * 1000) : null;
+ Date creationDate = getPublicKey().getCreationTime();
+ Date expiryDate = getPublicKey().getExpiryTime();
Date now = new Date();
return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
index 3539a4ceb..303070333 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
@@ -20,8 +20,16 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
/** Wrapper for a PGPPublicKey.
*
@@ -53,7 +61,7 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
public boolean canSign() {
// if key flags subpacket is available, honor it!
- if (getKeyUsage() != null) {
+ if (getKeyUsage() != 0) {
return (getKeyUsage() & KeyFlags.SIGN_DATA) != 0;
}
@@ -66,7 +74,7 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
public boolean canCertify() {
// if key flags subpacket is available, honor it!
- if (getKeyUsage() != null) {
+ if (getKeyUsage() != 0) {
return (getKeyUsage() & KeyFlags.CERTIFY_OTHER) != 0;
}
@@ -79,7 +87,7 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
public boolean canEncrypt() {
// if key flags subpacket is available, honor it!
- if (getKeyUsage() != null) {
+ if (getKeyUsage() != 0) {
return (getKeyUsage() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0;
}
@@ -93,13 +101,79 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
public boolean canAuthenticate() {
// if key flags subpacket is available, honor it!
- if (getKeyUsage() != null) {
+ if (getKeyUsage() != 0) {
return (getKeyUsage() & KeyFlags.AUTHENTICATION) != 0;
}
return false;
}
+ public boolean isRevoked() {
+ return mPublicKey.getSignaturesOfType(isMasterKey()
+ ? PGPSignature.KEY_REVOCATION
+ : PGPSignature.SUBKEY_REVOCATION).hasNext();
+ }
+
+ public boolean isExpired () {
+ Date expiry = getExpiryTime();
+ return expiry != null && expiry.before(new Date());
+ }
+
+ public long getValidSeconds() {
+
+ long seconds;
+
+ // the getValidSeconds method is unreliable for master keys. we need to iterate all
+ // user ids, then use the most recent certification from a non-revoked user id
+ if (isMasterKey()) {
+ Date latestCreation = null;
+ seconds = 0;
+
+ for (byte[] rawUserId : getUnorderedRawUserIds()) {
+ Iterator<WrappedSignature> sigs = getSignaturesForRawId(rawUserId);
+
+ // there is always a certification, so this call is safe
+ WrappedSignature sig = sigs.next();
+
+ // we know a user id has at most two sigs: one certification, one revocation.
+ // if the sig is a revocation, or there is another sig (which is a revocation),
+ // the data in this uid is not relevant
+ if (sig.isRevocation() || sigs.hasNext()) {
+ continue;
+ }
+
+ // this is our revocation, UNLESS there is a newer certificate!
+ if (latestCreation == null || latestCreation.before(sig.getCreationTime())) {
+ latestCreation = sig.getCreationTime();
+ seconds = sig.getKeyExpirySeconds();
+ }
+ }
+ } else {
+ seconds = mPublicKey.getValidSeconds();
+ }
+
+ return seconds;
+ }
+
+ public Date getExpiryTime() {
+ long seconds = getValidSeconds();
+
+ if (seconds > Integer.MAX_VALUE) {
+ Log.e(Constants.TAG, "error, expiry time too large");
+ return null;
+ }
+ if (seconds == 0) {
+ // no expiry
+ return null;
+ }
+ Date creationDate = getCreationTime();
+ Calendar calendar = GregorianCalendar.getInstance();
+ calendar.setTime(creationDate);
+ calendar.add(Calendar.SECOND, (int) seconds);
+
+ return calendar.getTime();
+ }
+
/** Same method as superclass, but we make it public. */
public Integer getKeyUsage() {
return super.getKeyUsage();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
index 85ef3eaa4..c2506685d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
@@ -18,9 +18,11 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.util.IterableIterator;
@@ -76,7 +78,7 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
@SuppressWarnings("unchecked")
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
- return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
+ return new IterableIterator<>(new Iterator<CanonicalizedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
@@ -94,4 +96,13 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
});
}
+ /** Create a dummy secret ring from this key */
+ public UncachedKeyRing createDummySecretRing () {
+
+ PGPSecretKeyRing secRing = PGPSecretKeyRing.constructDummyFromPublic(getRing(),
+ S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY);
+ return new UncachedKeyRing(secRing);
+
+ }
+
} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index 6ccadac2e..40f2f48ad 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -182,7 +182,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
* @return
*/
public LinkedList<Integer> getSupportedHashAlgorithms() {
- LinkedList<Integer> supported = new LinkedList<Integer>();
+ LinkedList<Integer> supported = new LinkedList<>();
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
// No support for MD5
@@ -247,7 +247,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
int signatureType;
if (cleartext) {
- // for sign-only ascii text
+ // for sign-only ascii text (cleartext signature)
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
} else {
signatureType = PGPSignature.BINARY_DOCUMENT;
@@ -262,11 +262,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
signatureGenerator.setHashedSubpackets(spGen.generate());
return signatureGenerator;
- } catch (PgpKeyNotFoundException e) {
+ } catch (PgpKeyNotFoundException | PGPException e) {
// TODO: simply throw PGPException!
throw new PgpGeneralException("Error initializing signature!", e);
- } catch (PGPException e) {
- throw new PgpGeneralException("Error initializing signature!", e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
index eb589c3f9..b5f6a5b09 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
@@ -18,27 +18,19 @@
package org.sufficientlysecure.keychain.pgp;
-import org.spongycastle.bcpg.S2K;
-import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
-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.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
@@ -94,7 +86,7 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
public IterableIterator<CanonicalizedSecretKey> secretKeyIterator() {
final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
- return new IterableIterator<CanonicalizedSecretKey>(new Iterator<CanonicalizedSecretKey>() {
+ return new IterableIterator<>(new Iterator<CanonicalizedSecretKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
@@ -114,7 +106,7 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
- return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
+ return new IterableIterator<>(new Iterator<CanonicalizedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
@@ -133,7 +125,7 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
}
public HashMap<String,String> getLocalNotationData() {
- HashMap<String,String> result = new HashMap<String,String>();
+ HashMap<String,String> result = new HashMap<>();
Iterator<PGPSignature> it = getRing().getPublicKey().getKeySignatures();
while (it.hasNext()) {
WrappedSignature sig = new WrappedSignature(it.next());
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
index aa324c7ed..46defebf7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
@@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.pgp;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.util.Log;
@@ -33,7 +32,7 @@ public class OpenPgpSignatureResultBuilder {
// OpenPgpSignatureResult
private boolean mSignatureOnly = false;
private String mPrimaryUserId;
- private ArrayList<String> mUserIds = new ArrayList<String>();
+ private ArrayList<String> mUserIds = new ArrayList<>();
private long mKeyId;
// builder
@@ -105,8 +104,8 @@ public class OpenPgpSignatureResultBuilder {
setUserIds(signingRing.getUnorderedUserIds());
// either master key is expired/revoked or this specific subkey is expired/revoked
- setKeyExpired(signingRing.isExpired() || signingKey.isExpired());
- setKeyRevoked(signingRing.isRevoked() || signingKey.isRevoked());
+ setKeyExpired(signingRing.isExpired() || signingKey.isMaybeExpired());
+ setKeyRevoked(signingRing.isRevoked() || signingKey.isMaybeRevoked());
}
public OpenPgpSignatureResult build() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index b58df085f..2ba0b6231 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -22,13 +22,13 @@ import android.content.Context;
import android.webkit.MimeTypeMap;
import org.openintents.openpgp.OpenPgpMetadata;
+import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream;
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;
@@ -36,10 +36,10 @@ import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
-import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
@@ -48,11 +48,15 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.BaseOperation;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -83,6 +87,9 @@ public class PgpDecryptVerify extends BaseOperation {
private Set<Long> mAllowedKeyIds;
private boolean mDecryptMetadataOnly;
private byte[] mDecryptedSessionKey;
+ private byte[] mDetachedSignature;
+ private String mRequiredSignerFingerprint;
+ private boolean mSignedLiteralData;
protected PgpDecryptVerify(Builder builder) {
super(builder.mContext, builder.mProviderHelper, builder.mProgressable);
@@ -96,6 +103,9 @@ public class PgpDecryptVerify extends BaseOperation {
this.mAllowedKeyIds = builder.mAllowedKeyIds;
this.mDecryptMetadataOnly = builder.mDecryptMetadataOnly;
this.mDecryptedSessionKey = builder.mDecryptedSessionKey;
+ this.mDetachedSignature = builder.mDetachedSignature;
+ this.mSignedLiteralData = builder.mSignedLiteralData;
+ this.mRequiredSignerFingerprint = builder.mRequiredSignerFingerprint;
}
public static class Builder {
@@ -103,15 +113,18 @@ public class PgpDecryptVerify extends BaseOperation {
private Context mContext;
private ProviderHelper mProviderHelper;
private InputData mData;
- private OutputStream mOutStream;
// optional
+ private OutputStream mOutStream = null;
private Progressable mProgressable = null;
private boolean mAllowSymmetricDecryption = true;
private String mPassphrase = null;
private Set<Long> mAllowedKeyIds = null;
private boolean mDecryptMetadataOnly = false;
private byte[] mDecryptedSessionKey = null;
+ private byte[] mDetachedSignature = null;
+ private String mRequiredSignerFingerprint = null;
+ private boolean mSignedLiteralData = false;
public Builder(Context context, ProviderHelper providerHelper,
Progressable progressable,
@@ -123,6 +136,24 @@ public class PgpDecryptVerify extends BaseOperation {
mOutStream = outStream;
}
+ /**
+ * This is used when verifying signed literals to check that they are signed with
+ * the required key
+ */
+ public Builder setRequiredSignerFingerprint(String fingerprint) {
+ mRequiredSignerFingerprint = fingerprint;
+ return this;
+ }
+
+ /**
+ * This is to force a mode where the message is just the signature key id and
+ * then a literal data packet; used in Keybase.io proofs
+ */
+ public Builder setSignedLiteralData(boolean signedLiteralData) {
+ mSignedLiteralData = signedLiteralData;
+ return this;
+ }
+
public Builder setAllowSymmetricDecryption(boolean allowSymmetricDecryption) {
mAllowSymmetricDecryption = allowSymmetricDecryption;
return this;
@@ -156,6 +187,14 @@ public class PgpDecryptVerify extends BaseOperation {
return this;
}
+ /**
+ * If detachedSignature != null, it will be used exclusively to verify the signature
+ */
+ public Builder setDetachedSignature(byte[] detachedSignature) {
+ mDetachedSignature = detachedSignature;
+ return this;
+ }
+
public PgpDecryptVerify build() {
return new PgpDecryptVerify(this);
}
@@ -166,22 +205,32 @@ public class PgpDecryptVerify extends BaseOperation {
*/
public DecryptVerifyResult execute() {
try {
- // automatically works with ascii armor input and binary
- InputStream in = PGPUtil.getDecoderStream(mData.getInputStream());
-
- if (in instanceof ArmoredInputStream) {
- ArmoredInputStream aIn = (ArmoredInputStream) in;
- // it is ascii armored
- Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine());
+ if (mDetachedSignature != null) {
+ Log.d(Constants.TAG, "Detached signature present, verifying with this signature only");
- if (aIn.isClearText()) {
- // a cleartext signature, verify it with the other method
- return verifyCleartextSignature(aIn, 0);
+ return verifyDetachedSignature(mData.getInputStream(), 0);
+ } else {
+ // automatically works with PGP ascii armor and PGP binary
+ InputStream in = PGPUtil.getDecoderStream(mData.getInputStream());
+
+ if (in instanceof ArmoredInputStream) {
+ ArmoredInputStream aIn = (ArmoredInputStream) in;
+ // it is ascii armored
+ Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine());
+
+ if (mSignedLiteralData) {
+ return verifySignedLiteralData(aIn, 0);
+ } else if (aIn.isClearText()) {
+ // a cleartext signature, verify it with the other method
+ return verifyCleartextSignature(aIn, 0);
+ } else {
+ // else: ascii armored encryption! go on...
+ return decryptVerify(in, 0);
+ }
+ } else {
+ return decryptVerify(in, 0);
}
- // else: ascii armored encryption! go on...
}
-
- return decryptVerify(in, 0);
} catch (PGPException e) {
Log.d(Constants.TAG, "PGPException", e);
OperationLog log = new OperationLog();
@@ -196,6 +245,136 @@ public class PgpDecryptVerify extends BaseOperation {
}
/**
+ * Verify Keybase.io style signed literal data
+ */
+ private DecryptVerifyResult verifySignedLiteralData(InputStream in, int indent) throws IOException, PGPException {
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_VL, indent);
+
+ // thinking that the proof-fetching operation is going to take most of the time
+ updateProgress(R.string.progress_reading_data, 75, 100);
+
+ JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
+ Object o = pgpF.nextObject();
+ if (o instanceof PGPCompressedData) {
+ log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1);
+
+ pgpF = new JcaPGPObjectFactory(((PGPCompressedData) o).getDataStream());
+ o = pgpF.nextObject();
+ updateProgress(R.string.progress_decompressing_data, 80, 100);
+ }
+
+ // all we want to see is a OnePassSignatureList followed by LiteralData
+ if (!(o instanceof PGPOnePassSignatureList)) {
+ log.add(LogType.MSG_VL_ERROR_MISSING_SIGLIST, indent);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ }
+ PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) o;
+
+ // go through all signatures (should be just one), make sure we have
+ // the key and it matches the one we’re looking for
+ CanonicalizedPublicKeyRing signingRing = null;
+ CanonicalizedPublicKey signingKey = null;
+ int signatureIndex = -1;
+ for (int i = 0; i < sigList.size(); ++i) {
+ try {
+ long sigKeyId = sigList.get(i).getKeyID();
+ signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
+ );
+ signingKey = signingRing.getPublicKey(sigKeyId);
+ signatureIndex = i;
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.d(Constants.TAG, "key not found, trying next signature...");
+ }
+ }
+
+ // there has to be a key, and it has to be the right one
+ if (signingKey == null) {
+ log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent);
+ Log.d(Constants.TAG, "Failed to find key in signed-literal message");
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ }
+
+ String fingerprint = KeyFormattingUtils.convertFingerprintToHex(signingRing.getFingerprint());
+ if (!(mRequiredSignerFingerprint.equals(fingerprint))) {
+ log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent);
+ Log.d(Constants.TAG, "Fingerprint mismatch; wanted " + mRequiredSignerFingerprint +
+ " got " + fingerprint + "!");
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ }
+
+ OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
+
+ PGPOnePassSignature signature = sigList.get(signatureIndex);
+ signatureResultBuilder.initValid(signingRing, signingKey);
+
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
+
+ o = pgpF.nextObject();
+
+ if (!(o instanceof PGPLiteralData)) {
+ log.add(LogType.MSG_VL_ERROR_MISSING_LITERAL, indent);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ }
+
+ PGPLiteralData literalData = (PGPLiteralData) o;
+
+ log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1);
+ updateProgress(R.string.progress_decrypting, 85, 100);
+
+ InputStream dataIn = literalData.getInputStream();
+
+ int length;
+ byte[] buffer = new byte[1 << 16];
+ while ((length = dataIn.read(buffer)) > 0) {
+ mOutStream.write(buffer, 0, length);
+ signature.update(buffer, 0, length);
+ }
+
+ updateProgress(R.string.progress_verifying_signature, 95, 100);
+ log.add(LogType.MSG_VL_CLEAR_SIGNATURE_CHECK, indent + 1);
+
+ PGPSignatureList signatureList = (PGPSignatureList) pgpF.nextObject();
+ PGPSignature messageSignature = signatureList.get(signatureIndex);
+
+ // these are not cleartext signatures!
+ // TODO: what about binary signatures?
+ signatureResultBuilder.setSignatureOnly(false);
+
+ // Verify signature and check binding signatures
+ boolean validSignature = signature.verify(messageSignature);
+ if (validSignature) {
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
+ } else {
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
+ }
+ signatureResultBuilder.setValidSignature(validSignature);
+
+ OpenPgpSignatureResult signatureResult = signatureResultBuilder.build();
+
+ if (signatureResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED
+ && signatureResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED) {
+ log.add(LogType.MSG_VL_ERROR_INTEGRITY_CHECK, indent);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ }
+
+ updateProgress(R.string.progress_done, 100, 100);
+
+ log.add(LogType.MSG_VL_OK, indent);
+
+ // Return a positive result, with metadata and verification info
+ DecryptVerifyResult result =
+ new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ result.setSignatureResult(signatureResult);
+ return result;
+ }
+
+
+ /**
* Decrypt and/or verifies binary or ascii armored pgp
*/
private DecryptVerifyResult decryptVerify(InputStream in, int indent) throws IOException, PGPException {
@@ -205,7 +384,7 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC, indent);
indent += 1;
- PGPObjectFactory pgpF = new PGPObjectFactory(in, new JcaKeyFingerprintCalculator());
+ JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
@@ -234,6 +413,25 @@ public class PgpDecryptVerify extends BaseOperation {
boolean symmetricPacketFound = false;
boolean anyPacketFound = false;
+ // If the input stream is armored, and there is a charset specified, take a note for later
+ // https://tools.ietf.org/html/rfc4880#page56
+ String charset = null;
+ if (in instanceof ArmoredInputStream) {
+ ArmoredInputStream aIn = (ArmoredInputStream) in;
+ if (aIn.getArmorHeaders() != null) {
+ for (String header : aIn.getArmorHeaders()) {
+ String[] pieces = header.split(":", 2);
+ if (pieces.length == 2 && "charset".equalsIgnoreCase(pieces[0])) {
+ charset = pieces[1].trim();
+ break;
+ }
+ }
+ if (charset != null) {
+ log.add(LogType.MSG_DC_CHARSET, indent, charset);
+ }
+ }
+ }
+
// go through all objects and find one we can decrypt
while (it.hasNext()) {
Object obj = it.next();
@@ -257,19 +455,19 @@ public class PgpDecryptVerify extends BaseOperation {
);
} catch (ProviderHelper.NotFoundException e) {
// continue with the next packet in the while loop
- log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent +1);
+ log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
continue;
}
if (secretKeyRing == null) {
// continue with the next packet in the while loop
- log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent +1);
+ log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
continue;
}
// get subkey which has been used for this encryption packet
secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId);
if (secretEncryptionKey == null) {
// should actually never happen, so no need to be more specific.
- log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent +1);
+ log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
continue;
}
@@ -283,7 +481,7 @@ public class PgpDecryptVerify extends BaseOperation {
if (!mAllowedKeyIds.contains(masterKeyId)) {
// this key is in our db, but NOT allowed!
// continue with the next packet in the while loop
- log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent +1);
+ log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1);
continue;
}
}
@@ -298,15 +496,15 @@ public class PgpDecryptVerify extends BaseOperation {
try {
// returns "" if key has no passphrase
mPassphrase = getCachedPassphrase(subKeyId);
- log.add(LogType.MSG_DC_PASS_CACHED, indent +1);
+ log.add(LogType.MSG_DC_PASS_CACHED, indent + 1);
} catch (PassphraseCacheInterface.NoSecretKeyException e) {
- log.add(LogType.MSG_DC_ERROR_NO_KEY, indent +1);
+ log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
// if passphrase was not cached, return here indicating that a passphrase is missing!
if (mPassphrase == null) {
- log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent +1);
+ log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE, log);
result.setKeyIdPassphraseNeeded(subKeyId);
@@ -322,8 +520,8 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_SYM, indent);
- if (! mAllowSymmetricDecryption) {
- log.add(LogType.MSG_DC_SYM_SKIP, indent +1);
+ if (!mAllowSymmetricDecryption) {
+ log.add(LogType.MSG_DC_SYM_SKIP, indent + 1);
continue;
}
@@ -338,7 +536,7 @@ public class PgpDecryptVerify extends BaseOperation {
// if no passphrase is given, return here
// indicating that a passphrase is missing!
if (mPassphrase == null) {
- log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent +1);
+ log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE, log);
}
@@ -383,13 +581,13 @@ public class PgpDecryptVerify extends BaseOperation {
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
try {
- log.add(LogType.MSG_DC_UNLOCKING, indent +1);
+ log.add(LogType.MSG_DC_UNLOCKING, indent + 1);
if (!secretEncryptionKey.unlock(mPassphrase)) {
- log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent +1);
+ log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
} catch (PgpGeneralException e) {
- log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent +1);
+ log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
@@ -401,7 +599,7 @@ public class PgpDecryptVerify extends BaseOperation {
= secretEncryptionKey.getDecryptorFactory(mDecryptedSessionKey);
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
} catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {
- log.add(LogType.MSG_DC_PENDING_NFC, indent +1);
+ log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_NFC, log);
result.setNfcState(secretEncryptionKey.getKeyId(), e.encryptedSessionKey, mPassphrase);
@@ -412,11 +610,11 @@ public class PgpDecryptVerify extends BaseOperation {
// If we didn't find any useful data, error out
// no packet has been found where we have the corresponding secret key in our db
log.add(
- anyPacketFound ? LogType.MSG_DC_ERROR_NO_KEY : LogType.MSG_DC_ERROR_NO_DATA, indent +1);
+ anyPacketFound ? LogType.MSG_DC_ERROR_NO_KEY : LogType.MSG_DC_ERROR_NO_DATA, indent + 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
- PGPObjectFactory plainFact = new PGPObjectFactory(clear, new JcaKeyFingerprintCalculator());
+ JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
int signatureIndex = -1;
@@ -427,25 +625,27 @@ public class PgpDecryptVerify extends BaseOperation {
indent += 1;
if (dataChunk instanceof PGPCompressedData) {
- log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent +1);
+ log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1);
currentProgress += 2;
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
PGPCompressedData compressedData = (PGPCompressedData) dataChunk;
- PGPObjectFactory fact = new PGPObjectFactory(compressedData.getDataStream(), new JcaKeyFingerprintCalculator());
+ JcaPGPObjectFactory fact = new JcaPGPObjectFactory(compressedData.getDataStream());
dataChunk = fact.nextObject();
plainFact = fact;
}
PGPOnePassSignature signature = null;
if (dataChunk instanceof PGPOnePassSignatureList) {
- log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent +1);
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent + 1);
currentProgress += 2;
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
+ // NOTE: following code is similar to processSignature, but for PGPOnePassSignature
+
// go through all signatures
// and find out for which signature we have a key in our database
for (int i = 0; i < sigList.size(); ++i) {
@@ -491,19 +691,15 @@ public class PgpDecryptVerify extends BaseOperation {
OpenPgpMetadata metadata;
if (dataChunk instanceof PGPLiteralData) {
- log.add(LogType.MSG_DC_CLEAR_DATA, indent +1);
+ log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1);
indent += 2;
currentProgress += 4;
updateProgress(R.string.progress_decrypting, currentProgress, 100);
PGPLiteralData literalData = (PGPLiteralData) dataChunk;
- // TODO: how to get the real original size?
- // this is the encrypted size so if we enable compression this value is wrong!
- long originalSize = mData.getSize() - mData.getStreamPosition();
- if (originalSize < 0) {
- originalSize = 0;
- }
+ // reported size may be null if partial packets are involved (highly unlikely though)
+ Long originalSize = literalData.getDataLengthIfAvailable();
String originalFilename = literalData.getFileName();
String mimeType = null;
@@ -531,18 +727,20 @@ public class PgpDecryptVerify extends BaseOperation {
originalFilename,
mimeType,
literalData.getModificationTime().getTime(),
- originalSize);
+ originalSize == null ? 0 : originalSize);
- if ( ! originalFilename.equals("")) {
+ if (!"".equals(originalFilename)) {
log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename);
}
- log.add(LogType.MSG_DC_CLEAR_META_MIME, indent +1,
+ log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1,
mimeType);
- log.add(LogType.MSG_DC_CLEAR_META_TIME, indent +1,
+ log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1,
new Date(literalData.getModificationTime().getTime()).toString());
- if (originalSize != 0) {
+ if (originalSize != null) {
log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1,
Long.toString(originalSize));
+ } else {
+ log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1);
}
// return here if we want to decrypt the metadata only
@@ -550,6 +748,7 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_OK_META_ONLY, indent);
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ result.setCharset(charset);
result.setDecryptMetadata(metadata);
return result;
}
@@ -572,7 +771,10 @@ public class PgpDecryptVerify extends BaseOperation {
int length;
byte[] buffer = new byte[1 << 16];
while ((length = dataIn.read(buffer)) > 0) {
- mOutStream.write(buffer, 0, length);
+ Log.d(Constants.TAG, "read bytes: " + length);
+ if (mOutStream != null) {
+ mOutStream.write(buffer, 0, length);
+ }
// update signature buffer if signature is also present
if (signature != null) {
@@ -587,9 +789,8 @@ public class PgpDecryptVerify extends BaseOperation {
progress = 100;
}
progressScaler.setProgress((int) progress, 100);
- } else {
- // TODO: slow annealing to fake a progress?
}
+ // TODO: slow annealing to fake a progress?
}
if (signature != null) {
@@ -606,9 +807,9 @@ public class PgpDecryptVerify extends BaseOperation {
// Verify signature and check binding signatures
boolean validSignature = signature.verify(messageSignature);
if (validSignature) {
- log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent +1);
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
} else {
- log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent +1);
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
}
signatureResultBuilder.setValidSignature(validSignature);
}
@@ -632,6 +833,7 @@ public class PgpDecryptVerify extends BaseOperation {
// If no valid signature is present:
// Handle missing integrity protection like failed integrity protection!
// The MDC packet can be stripped by an attacker!
+ Log.d(Constants.TAG, "MDC fail");
if (!signatureResultBuilder.isValidSignature()) {
log.add(LogType.MSG_DC_ERROR_INTEGRITY_MISSING, indent);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
@@ -647,6 +849,7 @@ public class PgpDecryptVerify extends BaseOperation {
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setDecryptMetadata(metadata);
result.setSignatureResult(signatureResultBuilder.build());
+ result.setCharset(charset);
return result;
}
@@ -669,7 +872,7 @@ public class PgpDecryptVerify extends BaseOperation {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- updateProgress(R.string.progress_done, 0, 100);
+ updateProgress(R.string.progress_reading_data, 0, 100);
ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
int lookAhead = readInputLine(lineOut, aIn);
@@ -689,10 +892,12 @@ public class PgpDecryptVerify extends BaseOperation {
out.close();
byte[] clearText = out.toByteArray();
- mOutStream.write(clearText);
+ if (mOutStream != null) {
+ mOutStream.write(clearText);
+ }
updateProgress(R.string.progress_processing_signature, 60, 100);
- PGPObjectFactory pgpFact = new PGPObjectFactory(aIn, new JcaKeyFingerprintCalculator());
+ JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(aIn);
PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
if (sigList == null) {
@@ -700,45 +905,7 @@ public class PgpDecryptVerify extends BaseOperation {
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
- CanonicalizedPublicKeyRing signingRing = null;
- CanonicalizedPublicKey signingKey = null;
- int signatureIndex = -1;
-
- // go through all signatures
- // and find out for which signature we have a key in our database
- for (int i = 0; i < sigList.size(); ++i) {
- try {
- long sigKeyId = sigList.get(i).getKeyID();
- signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
- );
- signingKey = signingRing.getPublicKey(sigKeyId);
- signatureIndex = i;
- } catch (ProviderHelper.NotFoundException e) {
- Log.d(Constants.TAG, "key not found, trying next signature...");
- }
- }
-
- PGPSignature signature = null;
-
- if (signingKey != null) {
- // key found in our database!
- signature = sigList.get(signatureIndex);
-
- signatureResultBuilder.initValid(signingRing, signingKey);
-
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
- } else {
- // no key in our database -> return "unknown pub key" status including the first key id
- if (!sigList.isEmpty()) {
- signatureResultBuilder.setSignatureAvailable(true);
- signatureResultBuilder.setKnownKey(false);
- signatureResultBuilder.setKeyId(sigList.get(0).getKeyID());
- }
- }
+ PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder);
if (signature != null) {
try {
@@ -786,6 +953,133 @@ public class PgpDecryptVerify extends BaseOperation {
return result;
}
+ private DecryptVerifyResult verifyDetachedSignature(InputStream in, int indent)
+ throws IOException, PGPException {
+
+ OperationLog log = new OperationLog();
+
+ OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
+ // detached signatures are never encrypted
+ signatureResultBuilder.setSignatureOnly(true);
+
+ updateProgress(R.string.progress_processing_signature, 0, 100);
+ InputStream detachedSigIn = new ByteArrayInputStream(mDetachedSignature);
+ detachedSigIn = PGPUtil.getDecoderStream(detachedSigIn);
+
+ JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(detachedSigIn);
+
+ PGPSignatureList sigList;
+ Object o = pgpFact.nextObject();
+ if (o instanceof PGPCompressedData) {
+ PGPCompressedData c1 = (PGPCompressedData) o;
+ pgpFact = new JcaPGPObjectFactory(c1.getDataStream());
+ sigList = (PGPSignatureList) pgpFact.nextObject();
+ } else if (o instanceof PGPSignatureList) {
+ sigList = (PGPSignatureList) o;
+ } else {
+ log.add(LogType.MSG_DC_ERROR_INVALID_SIGLIST, 0);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ }
+
+ PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder);
+
+ if (signature != null) {
+ updateProgress(R.string.progress_reading_data, 60, 100);
+
+ ProgressScaler progressScaler = new ProgressScaler(mProgressable, 60, 90, 100);
+ long alreadyWritten = 0;
+ long wholeSize = mData.getSize() - mData.getStreamPosition();
+ int length;
+ byte[] buffer = new byte[1 << 16];
+ while ((length = in.read(buffer)) > 0) {
+ if (mOutStream != null) {
+ mOutStream.write(buffer, 0, length);
+ }
+
+ // update signature buffer if signature is also present
+ signature.update(buffer, 0, length);
+
+ alreadyWritten += length;
+ if (wholeSize > 0) {
+ long progress = 100 * alreadyWritten / wholeSize;
+ // stop at 100% for wrong file sizes...
+ if (progress > 100) {
+ progress = 100;
+ }
+ progressScaler.setProgress((int) progress, 100);
+ }
+ // TODO: slow annealing to fake a progress?
+ }
+
+ updateProgress(R.string.progress_verifying_signature, 90, 100);
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
+
+ // these are not cleartext signatures!
+ signatureResultBuilder.setSignatureOnly(false);
+
+ // Verify signature and check binding signatures
+ boolean validSignature = signature.verify();
+ if (validSignature) {
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
+ } else {
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
+ }
+ signatureResultBuilder.setValidSignature(validSignature);
+ }
+
+ updateProgress(R.string.progress_done, 100, 100);
+
+ log.add(LogType.MSG_DC_OK, indent);
+
+ DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ result.setSignatureResult(signatureResultBuilder.build());
+ return result;
+ }
+
+ private PGPSignature processPGPSignatureList(PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder) throws PGPException {
+ CanonicalizedPublicKeyRing signingRing = null;
+ CanonicalizedPublicKey signingKey = null;
+ int signatureIndex = -1;
+
+ // go through all signatures
+ // and find out for which signature we have a key in our database
+ for (int i = 0; i < sigList.size(); ++i) {
+ try {
+ long sigKeyId = sigList.get(i).getKeyID();
+ signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
+ );
+ signingKey = signingRing.getPublicKey(sigKeyId);
+ signatureIndex = i;
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.d(Constants.TAG, "key not found, trying next signature...");
+ }
+ }
+
+ PGPSignature signature = null;
+
+ if (signingKey != null) {
+ // key found in our database!
+ signature = sigList.get(signatureIndex);
+
+ signatureResultBuilder.initValid(signingRing, signingKey);
+
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
+ } else {
+ // no key in our database -> return "unknown pub key" status including the first key id
+ if (!sigList.isEmpty()) {
+ signatureResultBuilder.setSignatureAvailable(true);
+ signatureResultBuilder.setKnownKey(false);
+ signatureResultBuilder.setKeyId(sigList.get(0).getKeyID());
+ }
+ }
+
+ return signature;
+ }
+
/**
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
*/
@@ -807,7 +1101,7 @@ public class PgpDecryptVerify extends BaseOperation {
while ((ch = fIn.read()) >= 0) {
bOut.write(ch);
if (ch == '\r' || ch == '\n') {
- lookAhead = readPassedEOL(bOut, ch, fIn);
+ lookAhead = readPastEOL(bOut, ch, fIn);
break;
}
}
@@ -824,7 +1118,7 @@ public class PgpDecryptVerify extends BaseOperation {
do {
bOut.write(ch);
if (ch == '\r' || ch == '\n') {
- lookAhead = readPassedEOL(bOut, ch, fIn);
+ lookAhead = readPastEOL(bOut, ch, fIn);
break;
}
} while ((ch = fIn.read()) >= 0);
@@ -836,7 +1130,7 @@ public class PgpDecryptVerify extends BaseOperation {
return lookAhead;
}
- private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
+ private static int readPastEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
throws IOException {
int lookAhead = fIn.read();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 0a15fd98f..12de2f637 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -24,8 +24,8 @@ import android.content.pm.PackageManager.NameNotFoundException;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
import java.io.File;
import java.io.IOException;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 4bab7f2b9..1a251eb79 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -135,7 +135,7 @@ public class PgpKeyOperation {
public PgpKeyOperation(Progressable progress) {
super();
if (progress != null) {
- mProgress = new Stack<Progressable>();
+ mProgress = new Stack<>();
mProgress.push(progress);
}
}
@@ -288,13 +288,11 @@ public class PgpKeyOperation {
// build new key pair
return new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date());
- } catch(NoSuchProviderException e) {
+ } catch(NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
} catch(NoSuchAlgorithmException e) {
log.add(LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
return null;
- } catch(InvalidAlgorithmParameterException e) {
- throw new RuntimeException(e);
} catch(PGPException e) {
Log.e(Constants.TAG, "internal pgp error", e);
log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
@@ -389,6 +387,9 @@ public class PgpKeyOperation {
* with a passphrase fails, the operation will fail with an unlocking error. More specific
* handling of errors should be done in UI code!
*
+ * If the passphrase is null, only a restricted subset of operations will be available,
+ * namely stripping of subkeys and changing the protection mode of dummy keys.
+ *
*/
public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
String passphrase) {
@@ -429,12 +430,17 @@ public class PgpKeyOperation {
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
+ // If we have no passphrase, only allow restricted operation
+ if (passphrase == null) {
+ return internalRestricted(sKR, saveParcel, log);
+ }
+
// read masterKeyFlags, and use the same as before.
// since this is the master key, this contains at least CERTIFY_OTHER
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
int masterKeyFlags = readKeyFlags(masterPublicKey) | KeyFlags.CERTIFY_OTHER;
- long masterKeyExpiry = masterPublicKey.getValidSeconds() == 0L ? 0L :
- masterPublicKey.getCreationTime().getTime() / 1000 + masterPublicKey.getValidSeconds();
+ Date expiryTime = wsKR.getPublicKey().getExpiryTime();
+ long masterKeyExpiry = expiryTime != null ? expiryTime.getTime() / 1000 : 0L;
return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, saveParcel, passphrase, log);
@@ -496,7 +502,7 @@ public class PgpKeyOperation {
@SuppressWarnings("unchecked")
Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
if (it != null) {
- for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
+ for (PGPSignature cert : new IterableIterator<>(it)) {
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
// foreign certificate?! error error error
log.add(LogType.MSG_MF_ERROR_INTEGRITY, indent);
@@ -715,6 +721,27 @@ public class PgpKeyOperation {
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
+ if (change.mDummyStrip || change.mDummyDivert != null) {
+ // IT'S DANGEROUS~
+ // no really, it is. this operation irrevocably removes the private key data from the key
+ if (change.mDummyStrip) {
+ sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
+ } else {
+ // the serial number must be 16 bytes in length
+ if (change.mDummyDivert.length != 16) {
+ log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
+ indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+ }
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+ }
+
+ // This doesn't concern us any further
+ if (!change.mRecertify && (change.mExpiry == null && change.mFlags == null)) {
+ continue;
+ }
+
// expiry must not be in the past
if (change.mExpiry != null && change.mExpiry != 0 &&
new Date(change.mExpiry*1000).before(new Date())) {
@@ -805,30 +832,6 @@ public class PgpKeyOperation {
}
subProgressPop();
- // 4c. For each subkey to be stripped... do so
- subProgressPush(65, 70);
- for (int i = 0; i < saveParcel.mStripSubKeys.size(); i++) {
-
- progress(R.string.progress_modify_subkeystrip, (i-1) * (100 / saveParcel.mStripSubKeys.size()));
- long strip = saveParcel.mStripSubKeys.get(i);
- log.add(LogType.MSG_MF_SUBKEY_STRIP,
- indent, KeyFormattingUtils.convertKeyIdToHex(strip));
-
- PGPSecretKey sKey = sKR.getSecretKey(strip);
- if (sKey == null) {
- log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
- indent+1, KeyFormattingUtils.convertKeyIdToHex(strip));
- return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
- }
-
- // IT'S DANGEROUS~
- // no really, it is. this operation irrevocably removes the private key data from the key
- sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
- sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
-
- }
- subProgressPop();
-
// 5. Generate and add new subkeys
subProgressPush(70, 90);
for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) {
@@ -937,6 +940,73 @@ public class PgpKeyOperation {
}
+ /** This method does the actual modifications in a keyring just like internal, except it
+ * supports only the subset of operations which require no passphrase, and will error
+ * otherwise.
+ */
+ private PgpEditKeyResult internalRestricted(PGPSecretKeyRing sKR, SaveKeyringParcel saveParcel,
+ OperationLog log) {
+
+ int indent = 1;
+
+ progress(R.string.progress_modify, 0);
+
+ // Make sure the saveParcel includes only operations available without passphrae!
+ if (!saveParcel.isRestrictedOnly()) {
+ log.add(LogType.MSG_MF_ERROR_RESTRICTED, indent);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // Check if we were cancelled
+ if (checkCancelled()) {
+ log.add(LogType.MSG_OPERATION_CANCELLED, indent);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
+ }
+
+ // The only operation we can do here:
+ // 4a. Strip secret keys, or change their protection mode (stripped/divert-to-card)
+ subProgressPush(50, 60);
+ for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) {
+
+ progress(R.string.progress_modify_subkeychange, (i - 1) * (100 / saveParcel.mChangeSubKeys.size()));
+ SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i);
+ log.add(LogType.MSG_MF_SUBKEY_CHANGE,
+ indent, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
+
+ PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
+ if (sKey == null) {
+ log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
+ indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ if (change.mDummyStrip || change.mDummyDivert != null) {
+ // IT'S DANGEROUS~
+ // no really, it is. this operation irrevocably removes the private key data from the key
+ if (change.mDummyStrip) {
+ sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
+ } else {
+ // the serial number must be 16 bytes in length
+ if (change.mDummyDivert.length != 16) {
+ log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
+ indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+ sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mDummyDivert);
+ }
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+ }
+
+ }
+
+ // And we're done!
+ progress(R.string.progress_done, 100);
+ log.add(LogType.MSG_MF_SUCCESS, indent);
+ return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR));
+
+ }
+
+
private static PGPSecretKeyRing applyNewUnlock(
PGPSecretKeyRing sKR,
PGPPublicKey masterPublicKey,
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java
new file mode 100644
index 000000000..9318be006
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java
@@ -0,0 +1,176 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
+import org.sufficientlysecure.keychain.Constants;
+
+import java.util.Date;
+
+public class PgpSignEncryptInput {
+
+ protected String mVersionHeader = null;
+ protected boolean mEnableAsciiArmorOutput = false;
+ protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
+ protected long[] mEncryptionMasterKeyIds = null;
+ protected String mSymmetricPassphrase = null;
+ protected int mSymmetricEncryptionAlgorithm = 0;
+ protected long mSignatureMasterKeyId = Constants.key.none;
+ protected Long mSignatureSubKeyId = null;
+ protected int mSignatureHashAlgorithm = 0;
+ protected String mSignaturePassphrase = null;
+ protected long mAdditionalEncryptId = Constants.key.none;
+ protected byte[] mNfcSignedHash = null;
+ protected Date mNfcCreationTimestamp = null;
+ protected boolean mFailOnMissingEncryptionKeyIds = false;
+ protected String mCharset;
+ protected boolean mCleartextSignature;
+ protected boolean mDetachedSignature;
+
+ public String getCharset() {
+ return mCharset;
+ }
+
+ public void setCharset(String mCharset) {
+ this.mCharset = mCharset;
+ }
+
+ public boolean ismFailOnMissingEncryptionKeyIds() {
+ return mFailOnMissingEncryptionKeyIds;
+ }
+
+ public Date getNfcCreationTimestamp() {
+ return mNfcCreationTimestamp;
+ }
+
+ public byte[] getNfcSignedHash() {
+ return mNfcSignedHash;
+ }
+
+ public long getAdditionalEncryptId() {
+ return mAdditionalEncryptId;
+ }
+
+ public PgpSignEncryptInput setAdditionalEncryptId(long additionalEncryptId) {
+ mAdditionalEncryptId = additionalEncryptId;
+ return this;
+ }
+
+ public String getSignaturePassphrase() {
+ return mSignaturePassphrase;
+ }
+
+ public PgpSignEncryptInput setSignaturePassphrase(String signaturePassphrase) {
+ mSignaturePassphrase = signaturePassphrase;
+ return this;
+ }
+
+ public int getSignatureHashAlgorithm() {
+ return mSignatureHashAlgorithm;
+ }
+
+ public PgpSignEncryptInput setSignatureHashAlgorithm(int signatureHashAlgorithm) {
+ mSignatureHashAlgorithm = signatureHashAlgorithm;
+ return this;
+ }
+
+ public Long getSignatureSubKeyId() {
+ return mSignatureSubKeyId;
+ }
+
+ public PgpSignEncryptInput setSignatureSubKeyId(long signatureSubKeyId) {
+ mSignatureSubKeyId = signatureSubKeyId;
+ return this;
+ }
+
+ public long getSignatureMasterKeyId() {
+ return mSignatureMasterKeyId;
+ }
+
+ public PgpSignEncryptInput setSignatureMasterKeyId(long signatureMasterKeyId) {
+ mSignatureMasterKeyId = signatureMasterKeyId;
+ return this;
+ }
+
+ public int getSymmetricEncryptionAlgorithm() {
+ return mSymmetricEncryptionAlgorithm;
+ }
+
+ public PgpSignEncryptInput setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
+ mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
+ return this;
+ }
+
+ public String getSymmetricPassphrase() {
+ return mSymmetricPassphrase;
+ }
+
+ public PgpSignEncryptInput setSymmetricPassphrase(String symmetricPassphrase) {
+ mSymmetricPassphrase = symmetricPassphrase;
+ return this;
+ }
+
+ public long[] getEncryptionMasterKeyIds() {
+ return mEncryptionMasterKeyIds;
+ }
+
+ public PgpSignEncryptInput setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {
+ mEncryptionMasterKeyIds = encryptionMasterKeyIds;
+ return this;
+ }
+
+ public int getCompressionId() {
+ return mCompressionId;
+ }
+
+ public PgpSignEncryptInput setCompressionId(int compressionId) {
+ mCompressionId = compressionId;
+ return this;
+ }
+
+ public boolean ismEnableAsciiArmorOutput() {
+ return mEnableAsciiArmorOutput;
+ }
+
+ public String getVersionHeader() {
+ return mVersionHeader;
+ }
+
+ public PgpSignEncryptInput setVersionHeader(String versionHeader) {
+ mVersionHeader = versionHeader;
+ return this;
+ }
+
+ public PgpSignEncryptInput setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
+ mEnableAsciiArmorOutput = enableAsciiArmorOutput;
+ return this;
+ }
+
+ public PgpSignEncryptInput setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) {
+ mFailOnMissingEncryptionKeyIds = failOnMissingEncryptionKeyIds;
+ return this;
+ }
+
+ public PgpSignEncryptInput setNfcState(byte[] signedHash, Date creationTimestamp) {
+ mNfcSignedHash = signedHash;
+ mNfcCreationTimestamp = creationTimestamp;
+ return this;
+ }
+
+ public PgpSignEncryptInput setCleartextSignature(boolean cleartextSignature) {
+ this.mCleartextSignature = cleartextSignature;
+ return this;
+ }
+
+ public boolean isCleartextSignature() {
+ return mCleartextSignature;
+ }
+
+ public PgpSignEncryptInput setDetachedSignature(boolean detachedSignature) {
+ this.mDetachedSignature = detachedSignature;
+ return this;
+ }
+
+ public boolean isDetachedSignature() {
+ return mDetachedSignature;
+ }
+}
+
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
index 3c3bcc890..2fa01d241 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
@@ -25,6 +25,7 @@ import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.openpgp.PGPCompressedDataGenerator;
+import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPLiteralData;
@@ -37,19 +38,20 @@ import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.BaseOperation;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -59,31 +61,23 @@ import java.security.SignatureException;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
+import java.util.concurrent.atomic.AtomicBoolean;
-/**
- * This class uses a Builder pattern!
+/** This class supports a single, low-level, sign/encrypt operation.
+ *
+ * The operation of this class takes an Input- and OutputStream plus a
+ * PgpSignEncryptInput, and signs and/or encrypts the stream as
+ * parametrized in the PgpSignEncryptInput object. It returns its status
+ * and a possible detached signature as a SignEncryptResult.
+ *
+ * For a high-level operation based on URIs, see SignEncryptOperation.
+ *
+ * @see org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput
+ * @see org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult
+ * @see org.sufficientlysecure.keychain.operations.SignEncryptOperation
+ *
*/
-public class PgpSignEncrypt extends BaseOperation {
- private String mVersionHeader;
- private InputData mData;
- private OutputStream mOutStream;
-
- private boolean mEnableAsciiArmorOutput;
- private int mCompressionId;
- private long[] mEncryptionMasterKeyIds;
- private String mSymmetricPassphrase;
- private int mSymmetricEncryptionAlgorithm;
- private long mSignatureMasterKeyId;
- private Long mSignatureSubKeyId;
- private int mSignatureHashAlgorithm;
- private String mSignaturePassphrase;
- private long mAdditionalEncryptId;
- private boolean mCleartextInput;
- private String mOriginalFilename;
- private boolean mFailOnMissingEncryptionKeyIds;
-
- private byte[] mNfcSignedHash = null;
- private Date mNfcCreationTimestamp = null;
+public class PgpSignEncryptOperation extends BaseOperation {
private static byte[] NEW_LINE;
@@ -95,241 +89,107 @@ public class PgpSignEncrypt extends BaseOperation {
}
}
- protected PgpSignEncrypt(Builder builder) {
- super(builder.mContext, builder.mProviderHelper, builder.mProgressable);
-
- // private Constructor can only be called from Builder
- this.mVersionHeader = builder.mVersionHeader;
- this.mData = builder.mData;
- this.mOutStream = builder.mOutStream;
-
- this.mEnableAsciiArmorOutput = builder.mEnableAsciiArmorOutput;
- this.mCompressionId = builder.mCompressionId;
- this.mEncryptionMasterKeyIds = builder.mEncryptionMasterKeyIds;
- this.mSymmetricPassphrase = builder.mSymmetricPassphrase;
- this.mSymmetricEncryptionAlgorithm = builder.mSymmetricEncryptionAlgorithm;
- this.mSignatureMasterKeyId = builder.mSignatureMasterKeyId;
- this.mSignatureSubKeyId = builder.mSignatureSubKeyId;
- this.mSignatureHashAlgorithm = builder.mSignatureHashAlgorithm;
- this.mSignaturePassphrase = builder.mSignaturePassphrase;
- this.mAdditionalEncryptId = builder.mAdditionalEncryptId;
- this.mCleartextInput = builder.mCleartextInput;
- this.mNfcSignedHash = builder.mNfcSignedHash;
- this.mNfcCreationTimestamp = builder.mNfcCreationTimestamp;
- this.mOriginalFilename = builder.mOriginalFilename;
- this.mFailOnMissingEncryptionKeyIds = builder.mFailOnMissingEncryptionKeyIds;
+ public PgpSignEncryptOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
+ super(context, providerHelper, progressable, cancelled);
}
- public static class Builder {
- // mandatory parameter
- private Context mContext;
- private ProviderHelper mProviderHelper;
- private Progressable mProgressable;
- private InputData mData;
- private OutputStream mOutStream;
-
- // optional
- private String mVersionHeader = null;
- private boolean mEnableAsciiArmorOutput = false;
- private int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
- private long[] mEncryptionMasterKeyIds = null;
- private String mSymmetricPassphrase = null;
- private int mSymmetricEncryptionAlgorithm = 0;
- private long mSignatureMasterKeyId = Constants.key.none;
- private Long mSignatureSubKeyId = null;
- private int mSignatureHashAlgorithm = 0;
- private String mSignaturePassphrase = null;
- private long mAdditionalEncryptId = Constants.key.none;
- private boolean mCleartextInput = false;
- private String mOriginalFilename = "";
- private byte[] mNfcSignedHash = null;
- private Date mNfcCreationTimestamp = null;
- private boolean mFailOnMissingEncryptionKeyIds = false;
-
- public Builder(Context context, ProviderHelper providerHelper, Progressable progressable,
- InputData data, OutputStream outStream) {
- mContext = context;
- mProviderHelper = providerHelper;
- mProgressable = progressable;
-
- mData = data;
- mOutStream = outStream;
- }
-
- public Builder setVersionHeader(String versionHeader) {
- mVersionHeader = versionHeader;
- return this;
- }
-
- public Builder setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
- mEnableAsciiArmorOutput = enableAsciiArmorOutput;
- return this;
- }
-
- public Builder setCompressionId(int compressionId) {
- mCompressionId = compressionId;
- return this;
- }
-
- public Builder setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {
- mEncryptionMasterKeyIds = encryptionMasterKeyIds;
- return this;
- }
-
- public Builder setSymmetricPassphrase(String symmetricPassphrase) {
- mSymmetricPassphrase = symmetricPassphrase;
- return this;
- }
-
- public Builder setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
- mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
- return this;
- }
-
- public Builder setSignatureMasterKeyId(long signatureMasterKeyId) {
- mSignatureMasterKeyId = signatureMasterKeyId;
- return this;
- }
-
- public Builder setSignatureSubKeyId(long signatureSubKeyId) {
- mSignatureSubKeyId = signatureSubKeyId;
- return this;
- }
-
- public Builder setSignatureHashAlgorithm(int signatureHashAlgorithm) {
- mSignatureHashAlgorithm = signatureHashAlgorithm;
- return this;
- }
-
- public Builder setSignaturePassphrase(String signaturePassphrase) {
- mSignaturePassphrase = signaturePassphrase;
- return this;
- }
-
- public Builder setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) {
- mFailOnMissingEncryptionKeyIds = failOnMissingEncryptionKeyIds;
- return this;
- }
-
- /**
- * Also encrypt with the signing keyring
- *
- * @param additionalEncryptId
- * @return
- */
- public Builder setAdditionalEncryptId(long additionalEncryptId) {
- mAdditionalEncryptId = additionalEncryptId;
- return this;
- }
-
- /**
- * TODO: test this option!
- *
- * @param cleartextInput
- * @return
- */
- public Builder setCleartextInput(boolean cleartextInput) {
- mCleartextInput = cleartextInput;
- return this;
- }
-
- public Builder setOriginalFilename(String originalFilename) {
- mOriginalFilename = originalFilename;
- return this;
- }
-
- public Builder setNfcState(byte[] signedHash, Date creationTimestamp) {
- mNfcSignedHash = signedHash;
- mNfcCreationTimestamp = creationTimestamp;
- return this;
- }
-
- public PgpSignEncrypt build() {
- return new PgpSignEncrypt(this);
- }
+ public PgpSignEncryptOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
+ super(context, providerHelper, progressable);
}
/**
* Signs and/or encrypts data based on parameters of class
*/
- public SignEncryptResult execute() {
+ public PgpSignEncryptResult execute(PgpSignEncryptInput input,
+ InputData inputData, OutputStream outputStream) {
int indent = 0;
OperationLog log = new OperationLog();
- log.add(LogType.MSG_SE, indent);
+ log.add(LogType.MSG_PSE, indent);
indent += 1;
- boolean enableSignature = mSignatureMasterKeyId != Constants.key.none;
- boolean enableEncryption = ((mEncryptionMasterKeyIds != null && mEncryptionMasterKeyIds.length > 0)
- || mSymmetricPassphrase != null);
- boolean enableCompression = (mCompressionId != CompressionAlgorithmTags.UNCOMPRESSED);
+ boolean enableSignature = input.getSignatureMasterKeyId() != Constants.key.none;
+ boolean enableEncryption = ((input.getEncryptionMasterKeyIds() != null && input.getEncryptionMasterKeyIds().length > 0)
+ || input.getSymmetricPassphrase() != null);
+ boolean enableCompression = (input.getCompressionId() != CompressionAlgorithmTags.UNCOMPRESSED);
Log.d(Constants.TAG, "enableSignature:" + enableSignature
+ "\nenableEncryption:" + enableEncryption
+ "\nenableCompression:" + enableCompression
- + "\nenableAsciiArmorOutput:" + mEnableAsciiArmorOutput);
+ + "\nenableAsciiArmorOutput:" + input.ismEnableAsciiArmorOutput());
// add additional key id to encryption ids (mostly to do self-encryption)
- if (enableEncryption && mAdditionalEncryptId != Constants.key.none) {
- mEncryptionMasterKeyIds = Arrays.copyOf(mEncryptionMasterKeyIds, mEncryptionMasterKeyIds.length + 1);
- mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mAdditionalEncryptId;
+ if (enableEncryption && input.getAdditionalEncryptId() != Constants.key.none) {
+ input.setEncryptionMasterKeyIds(Arrays.copyOf(input.getEncryptionMasterKeyIds(), input.getEncryptionMasterKeyIds().length + 1));
+ input.getEncryptionMasterKeyIds()[input.getEncryptionMasterKeyIds().length - 1] = input.getAdditionalEncryptId();
}
ArmoredOutputStream armorOut = null;
OutputStream out;
- if (mEnableAsciiArmorOutput) {
- armorOut = new ArmoredOutputStream(mOutStream);
- if (mVersionHeader != null) {
- armorOut.setHeader("Version", mVersionHeader);
+ if (input.ismEnableAsciiArmorOutput()) {
+ armorOut = new ArmoredOutputStream(outputStream);
+ if (input.getVersionHeader() != null) {
+ armorOut.setHeader("Version", input.getVersionHeader());
+ }
+ // if we have a charset, put it in the header
+ if (input.getCharset() != null) {
+ armorOut.setHeader("Charset", input.getCharset());
}
out = armorOut;
} else {
- out = mOutStream;
+ out = outputStream;
}
/* Get keys for signature generation for later usage */
CanonicalizedSecretKey signingKey = null;
- long signKeyId;
if (enableSignature) {
try {
// fetch the indicated master key id (the one whose name we sign in)
CanonicalizedSecretKeyRing signingKeyRing =
- mProviderHelper.getCanonicalizedSecretKeyRing(mSignatureMasterKeyId);
+ mProviderHelper.getCanonicalizedSecretKeyRing(input.getSignatureMasterKeyId());
+
+ long signKeyId;
+ // use specified signing subkey, or find the one to use
+ if (input.getSignatureSubKeyId() == null) {
+ signKeyId = signingKeyRing.getSecretSignId();
+ } else {
+ signKeyId = input.getSignatureSubKeyId();
+ }
+
// fetch the specific subkey to sign with, or just use the master key if none specified
- signKeyId = mSignatureSubKeyId != null ? mSignatureSubKeyId : mSignatureMasterKeyId;
signingKey = signingKeyRing.getSecretKey(signKeyId);
- // make sure it's a signing key alright!
- } catch (ProviderHelper.NotFoundException e) {
- log.add(LogType.MSG_SE_ERROR_SIGN_KEY, indent);
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+
+ } catch (ProviderHelper.NotFoundException | PgpGeneralException e) {
+ log.add(LogType.MSG_PSE_ERROR_SIGN_KEY, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
// Make sure we are allowed to sign here!
if (!signingKey.canSign()) {
- log.add(LogType.MSG_SE_ERROR_KEY_SIGN, indent);
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ log.add(LogType.MSG_PSE_ERROR_KEY_SIGN, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
// if no passphrase was explicitly set try to get it from the cache service
- if (mSignaturePassphrase == null) {
+ if (input.getSignaturePassphrase() == null) {
try {
// returns "" if key has no passphrase
- mSignaturePassphrase = getCachedPassphrase(signKeyId);
+ input.setSignaturePassphrase(getCachedPassphrase(signingKey.getKeyId()));
// TODO
// log.add(LogType.MSG_DC_PASS_CACHED, indent + 1);
} catch (PassphraseCacheInterface.NoSecretKeyException e) {
// TODO
// log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1);
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
// if passphrase was not cached, return here indicating that a passphrase is missing!
- if (mSignaturePassphrase == null) {
- log.add(LogType.MSG_SE_PENDING_PASSPHRASE, indent + 1);
- SignEncryptResult result = new SignEncryptResult(SignEncryptResult.RESULT_PENDING_PASSPHRASE, log);
- result.setKeyIdPassphraseNeeded(signKeyId);
+ if (input.getSignaturePassphrase() == null) {
+ log.add(LogType.MSG_PSE_PENDING_PASSPHRASE, indent + 1);
+ PgpSignEncryptResult result = new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE, log);
+ result.setKeyIdPassphraseNeeded(signingKey.getKeyId());
return result;
}
}
@@ -337,20 +197,24 @@ public class PgpSignEncrypt extends BaseOperation {
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
try {
- if (!signingKey.unlock(mSignaturePassphrase)) {
- log.add(LogType.MSG_SE_ERROR_BAD_PASSPHRASE, indent);
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ if (!signingKey.unlock(input.getSignaturePassphrase())) {
+ log.add(LogType.MSG_PSE_ERROR_BAD_PASSPHRASE, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
} catch (PgpGeneralException e) {
- log.add(LogType.MSG_SE_ERROR_UNLOCK, indent);
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ log.add(LogType.MSG_PSE_ERROR_UNLOCK, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
// check if hash algo is supported
+ int requestedAlgorithm = input.getSignatureHashAlgorithm();
LinkedList<Integer> supported = signingKey.getSupportedHashAlgorithms();
- if (!supported.contains(mSignatureHashAlgorithm)) {
+ if (requestedAlgorithm == 0) {
// get most preferred
- mSignatureHashAlgorithm = supported.getLast();
+ input.setSignatureHashAlgorithm(supported.getLast());
+ } else if (!supported.contains(requestedAlgorithm)) {
+ log.add(LogType.MSG_PSE_ERROR_HASH_ALGO, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
}
updateProgress(R.string.progress_preparing_streams, 2, 100);
@@ -358,44 +222,48 @@ public class PgpSignEncrypt extends BaseOperation {
/* Initialize PGPEncryptedDataGenerator for later usage */
PGPEncryptedDataGenerator cPk = null;
if (enableEncryption) {
+ int algo = input.getSymmetricEncryptionAlgorithm();
+ if (algo == 0) {
+ algo = PGPEncryptedData.AES_128;
+ }
// has Integrity packet enabled!
JcePGPDataEncryptorBuilder encryptorBuilder =
- new JcePGPDataEncryptorBuilder(mSymmetricEncryptionAlgorithm)
+ new JcePGPDataEncryptorBuilder(algo)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
.setWithIntegrityPacket(true);
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
- if (mSymmetricPassphrase != null) {
+ if (input.getSymmetricPassphrase() != null) {
// Symmetric encryption
- log.add(LogType.MSG_SE_SYMMETRIC, indent);
+ log.add(LogType.MSG_PSE_SYMMETRIC, indent);
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
- new JcePBEKeyEncryptionMethodGenerator(mSymmetricPassphrase.toCharArray());
+ new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().toCharArray());
cPk.addMethod(symmetricEncryptionGenerator);
} else {
- log.add(LogType.MSG_SE_ASYMMETRIC, indent);
+ log.add(LogType.MSG_PSE_ASYMMETRIC, indent);
// Asymmetric encryption
- for (long id : mEncryptionMasterKeyIds) {
+ for (long id : input.getEncryptionMasterKeyIds()) {
try {
CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(id));
CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();
cPk.addMethod(key.getPubKeyEncryptionGenerator());
- log.add(LogType.MSG_SE_KEY_OK, indent + 1,
+ log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
} catch (PgpKeyNotFoundException e) {
- log.add(LogType.MSG_SE_KEY_WARN, indent + 1,
+ log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
- if (mFailOnMissingEncryptionKeyIds) {
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ if (input.ismFailOnMissingEncryptionKeyIds()) {
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
} catch (ProviderHelper.NotFoundException e) {
- log.add(LogType.MSG_SE_KEY_UNKNOWN, indent + 1,
+ log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
- if (mFailOnMissingEncryptionKeyIds) {
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ if (input.ismFailOnMissingEncryptionKeyIds()) {
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
}
}
@@ -408,12 +276,12 @@ public class PgpSignEncrypt extends BaseOperation {
updateProgress(R.string.progress_preparing_signature, 4, 100);
try {
- boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
+ boolean cleartext = input.isCleartextSignature() && input.ismEnableAsciiArmorOutput() && !enableEncryption;
signatureGenerator = signingKey.getSignatureGenerator(
- mSignatureHashAlgorithm, cleartext, mNfcSignedHash, mNfcCreationTimestamp);
+ input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp());
} catch (PgpGeneralException e) {
- log.add(LogType.MSG_SE_ERROR_NFC, indent);
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ log.add(LogType.MSG_PSE_ERROR_NFC, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
}
@@ -424,14 +292,18 @@ public class PgpSignEncrypt extends BaseOperation {
OutputStream encryptionOut = null;
BCPGOutputStream bcpgOut;
+ ByteArrayOutputStream detachedByteOut = null;
+ ArmoredOutputStream detachedArmorOut = null;
+ BCPGOutputStream detachedBcpgOut = null;
+
try {
if (enableEncryption) {
/* actual encryption */
updateProgress(R.string.progress_encrypting, 8, 100);
log.add(enableSignature
- ? LogType.MSG_SE_SIGCRYPTING
- : LogType.MSG_SE_ENCRYPTING,
+ ? LogType.MSG_PSE_SIGCRYPTING
+ : LogType.MSG_PSE_ENCRYPTING,
indent
);
indent += 1;
@@ -439,8 +311,8 @@ public class PgpSignEncrypt extends BaseOperation {
encryptionOut = cPk.open(out, new byte[1 << 16]);
if (enableCompression) {
- log.add(LogType.MSG_SE_COMPRESSING, indent);
- compressGen = new PGPCompressedDataGenerator(mCompressionId);
+ log.add(LogType.MSG_PSE_COMPRESSING, indent);
+ compressGen = new PGPCompressedDataGenerator(input.getCompressionId());
bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
} else {
bcpgOut = new BCPGOutputStream(encryptionOut);
@@ -452,18 +324,18 @@ public class PgpSignEncrypt extends BaseOperation {
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
char literalDataFormatTag;
- if (mCleartextInput) {
+ if (input.isCleartextSignature()) {
literalDataFormatTag = PGPLiteralData.UTF8;
} else {
literalDataFormatTag = PGPLiteralData.BINARY;
}
- pOut = literalGen.open(bcpgOut, literalDataFormatTag, mOriginalFilename, new Date(),
- new byte[1 << 16]);
+ pOut = literalGen.open(bcpgOut, literalDataFormatTag,
+ inputData.getOriginalFilename(), new Date(), new byte[1 << 16]);
long alreadyWritten = 0;
int length;
byte[] buffer = new byte[1 << 16];
- InputStream in = mData.getInputStream();
+ InputStream in = inputData.getInputStream();
while ((length = in.read(buffer)) > 0) {
pOut.write(buffer, 0, length);
@@ -473,8 +345,8 @@ public class PgpSignEncrypt extends BaseOperation {
}
alreadyWritten += length;
- if (mData.getSize() > 0) {
- long progress = 100 * alreadyWritten / mData.getSize();
+ if (inputData.getSize() > 0) {
+ long progress = 100 * alreadyWritten / inputData.getSize();
progressScaler.setProgress((int) progress, 100);
}
}
@@ -482,16 +354,16 @@ public class PgpSignEncrypt extends BaseOperation {
literalGen.close();
indent -= 1;
- } else if (enableSignature && mCleartextInput && mEnableAsciiArmorOutput) {
+ } else if (enableSignature && input.isCleartextSignature() && input.ismEnableAsciiArmorOutput()) {
/* cleartext signature: sign-only of ascii text */
updateProgress(R.string.progress_signing, 8, 100);
- log.add(LogType.MSG_SE_SIGNING, indent);
+ log.add(LogType.MSG_PSE_SIGNING_CLEARTEXT, indent);
// write -----BEGIN PGP SIGNED MESSAGE-----
- armorOut.beginClearText(mSignatureHashAlgorithm);
+ armorOut.beginClearText(input.getSignatureHashAlgorithm());
- InputStream in = mData.getInputStream();
+ InputStream in = inputData.getInputStream();
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// update signature buffer with first line
@@ -517,16 +389,53 @@ public class PgpSignEncrypt extends BaseOperation {
armorOut.endClearText();
pOut = new BCPGOutputStream(armorOut);
- } else if (enableSignature && !mCleartextInput) {
+ } else if (enableSignature && input.isDetachedSignature()) {
+ /* detached signature */
+
+ updateProgress(R.string.progress_signing, 8, 100);
+ log.add(LogType.MSG_PSE_SIGNING_DETACHED, indent);
+
+ InputStream in = inputData.getInputStream();
+
+ // handle output stream separately for detached signatures
+ detachedByteOut = new ByteArrayOutputStream();
+ OutputStream detachedOut = detachedByteOut;
+ if (input.ismEnableAsciiArmorOutput()) {
+ detachedArmorOut = new ArmoredOutputStream(detachedOut);
+ if (input.getVersionHeader() != null) {
+ detachedArmorOut.setHeader("Version", input.getVersionHeader());
+ }
+
+ detachedOut = detachedArmorOut;
+ }
+ detachedBcpgOut = new BCPGOutputStream(detachedOut);
+
+ long alreadyWritten = 0;
+ int length;
+ byte[] buffer = new byte[1 << 16];
+ while ((length = in.read(buffer)) > 0) {
+ // no output stream is written, no changed to original data!
+
+ signatureGenerator.update(buffer, 0, length);
+
+ alreadyWritten += length;
+ if (inputData.getSize() > 0) {
+ long progress = 100 * alreadyWritten / inputData.getSize();
+ progressScaler.setProgress((int) progress, 100);
+ }
+ }
+
+ pOut = null;
+ } else if (enableSignature && !input.isCleartextSignature() && !input.isDetachedSignature()) {
/* sign-only binary (files/data stream) */
updateProgress(R.string.progress_signing, 8, 100);
- log.add(LogType.MSG_SE_ENCRYPTING, indent);
+ log.add(LogType.MSG_PSE_SIGNING, indent);
- InputStream in = mData.getInputStream();
+ InputStream in = inputData.getInputStream();
if (enableCompression) {
- compressGen = new PGPCompressedDataGenerator(mCompressionId);
+ compressGen = new PGPCompressedDataGenerator(input.getCompressionId());
bcpgOut = new BCPGOutputStream(compressGen.open(out));
} else {
bcpgOut = new BCPGOutputStream(out);
@@ -535,7 +444,8 @@ public class PgpSignEncrypt extends BaseOperation {
signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
- pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, mOriginalFilename, new Date(),
+ pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY,
+ inputData.getOriginalFilename(), new Date(),
new byte[1 << 16]);
long alreadyWritten = 0;
@@ -547,8 +457,8 @@ public class PgpSignEncrypt extends BaseOperation {
signatureGenerator.update(buffer, 0, length);
alreadyWritten += length;
- if (mData.getSize() > 0) {
- long progress = 100 * alreadyWritten / mData.getSize();
+ if (inputData.getSize() > 0) {
+ long progress = 100 * alreadyWritten / inputData.getSize();
progressScaler.setProgress((int) progress, 100);
}
}
@@ -556,61 +466,89 @@ public class PgpSignEncrypt extends BaseOperation {
literalGen.close();
} else {
pOut = null;
- log.add(LogType.MSG_SE_CLEARSIGN_ONLY, indent);
+ // TODO: Is this log right?
+ log.add(LogType.MSG_PSE_CLEARSIGN_ONLY, indent);
}
if (enableSignature) {
updateProgress(R.string.progress_generating_signature, 95, 100);
try {
- signatureGenerator.generate().encode(pOut);
+ if (detachedBcpgOut != null) {
+ signatureGenerator.generate().encode(detachedBcpgOut);
+ } else {
+ signatureGenerator.generate().encode(pOut);
+ }
} catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) {
// this secret key diverts to a OpenPGP card, throw exception with hash that will be signed
- log.add(LogType.MSG_SE_PENDING_NFC, indent);
- SignEncryptResult result =
- new SignEncryptResult(SignEncryptResult.RESULT_PENDING_NFC, log);
+ log.add(LogType.MSG_PSE_PENDING_NFC, indent);
+ PgpSignEncryptResult result =
+ new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_PENDING_NFC, log);
// Note that the checked key here is the master key, not the signing key
// (although these are always the same on Yubikeys)
- result.setNfcData(mSignatureSubKeyId, e.hashToSign, e.hashAlgo, e.creationTimestamp, mSignaturePassphrase);
- Log.d(Constants.TAG, "e.hashToSign"+ Hex.toHexString(e.hashToSign));
+ result.setNfcData(input.getSignatureSubKeyId(), e.hashToSign, e.hashAlgo, e.creationTimestamp, input.getSignaturePassphrase());
+ Log.d(Constants.TAG, "e.hashToSign" + Hex.toHexString(e.hashToSign));
return result;
}
}
// closing outputs
// NOTE: closing needs to be done in the correct order!
- // TODO: closing bcpgOut and pOut???
- if (enableEncryption) {
- if (enableCompression) {
+ if (encryptionOut != null) {
+ if (compressGen != null) {
compressGen.close();
}
encryptionOut.close();
}
- if (mEnableAsciiArmorOutput) {
+ // Note: Closing ArmoredOutputStream does not close the underlying stream
+ if (armorOut != null) {
armorOut.close();
}
-
- out.close();
- mOutStream.close();
+ // Note: Closing ArmoredOutputStream does not close the underlying stream
+ if (detachedArmorOut != null) {
+ detachedArmorOut.close();
+ }
+ // Also closes detachedBcpgOut
+ if (detachedByteOut != null) {
+ detachedByteOut.close();
+ }
+ if (out != null) {
+ out.close();
+ }
+ if (outputStream != null) {
+ outputStream.close();
+ }
} catch (SignatureException e) {
- log.add(LogType.MSG_SE_ERROR_SIG, indent);
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ log.add(LogType.MSG_PSE_ERROR_SIG, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
} catch (PGPException e) {
- log.add(LogType.MSG_SE_ERROR_PGP, indent);
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ log.add(LogType.MSG_PSE_ERROR_PGP, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
} catch (IOException e) {
- log.add(LogType.MSG_SE_ERROR_IO, indent);
- return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log);
+ log.add(LogType.MSG_PSE_ERROR_IO, indent);
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
updateProgress(R.string.progress_done, 100, 100);
- log.add(LogType.MSG_SE_OK, indent);
- return new SignEncryptResult(SignEncryptResult.RESULT_OK, log);
-
+ log.add(LogType.MSG_PSE_OK, indent);
+ PgpSignEncryptResult result = new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_OK, log);
+ if (detachedByteOut != null) {
+ try {
+ detachedByteOut.flush();
+ detachedByteOut.close();
+ } catch (IOException e) {
+ // silently catch
+ }
+ result.setDetachedSignature(detachedByteOut.toByteArray());
+ }
+ return result;
}
+ /**
+ * Remove whitespaces on line endings
+ */
private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
final PGPSignatureGenerator pSignatureGenerator)
throws IOException, SignatureException {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java
new file mode 100644
index 000000000..a4ed33397
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java
@@ -0,0 +1,135 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/** This parcel stores the input of one or more PgpSignEncrypt operations.
+ * All operations will use the same general paramters, differing only in
+ * input and output. Each input/output set depends on the paramters:
+ *
+ * - Each input uri is individually encrypted/signed
+ * - If a byte array is supplied, it is treated as an input before uris are processed
+ * - The number of output uris must match the number of input uris, plus one more
+ * if there is a byte array present.
+ * - Once the output uris are empty, there must be exactly one input (uri xor bytes)
+ * left, which will be returned in a byte array as part of the result parcel.
+ *
+ */
+public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable {
+
+ public ArrayList<Uri> mInputUris = new ArrayList<>();
+ public ArrayList<Uri> mOutputUris = new ArrayList<>();
+ public byte[] mBytes;
+
+ public SignEncryptParcel() {
+ super();
+ }
+
+ public SignEncryptParcel(Parcel src) {
+
+ // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
+ mVersionHeader = src.readString();
+ mEnableAsciiArmorOutput = src.readInt() == 1;
+ mCompressionId = src.readInt();
+ mEncryptionMasterKeyIds = src.createLongArray();
+ mSymmetricPassphrase = src.readString();
+ mSymmetricEncryptionAlgorithm = src.readInt();
+ mSignatureMasterKeyId = src.readLong();
+ mSignatureSubKeyId = src.readInt() == 1 ? src.readLong() : null;
+ mSignatureHashAlgorithm = src.readInt();
+ mSignaturePassphrase = src.readString();
+ mAdditionalEncryptId = src.readLong();
+ mNfcSignedHash = src.createByteArray();
+ mNfcCreationTimestamp = src.readInt() == 1 ? new Date(src.readLong()) : null;
+ mFailOnMissingEncryptionKeyIds = src.readInt() == 1;
+ mCharset = src.readString();
+ mCleartextSignature = src.readInt() == 1;
+ mDetachedSignature = src.readInt() == 1;
+
+ mInputUris = src.createTypedArrayList(Uri.CREATOR);
+ mOutputUris = src.createTypedArrayList(Uri.CREATOR);
+ mBytes = src.createByteArray();
+
+ }
+
+ public byte[] getBytes() {
+ return mBytes;
+ }
+
+ public void setBytes(byte[] bytes) {
+ mBytes = bytes;
+ }
+
+ public List<Uri> getInputUris() {
+ return Collections.unmodifiableList(mInputUris);
+ }
+
+ public void addInputUris(Collection<Uri> inputUris) {
+ mInputUris.addAll(inputUris);
+ }
+
+ public List<Uri> getOutputUris() {
+ return Collections.unmodifiableList(mOutputUris);
+ }
+
+ public void addOutputUris(ArrayList<Uri> outputUris) {
+ mOutputUris.addAll(outputUris);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mVersionHeader);
+ dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
+ dest.writeInt(mCompressionId);
+ dest.writeLongArray(mEncryptionMasterKeyIds);
+ dest.writeString(mSymmetricPassphrase);
+ dest.writeInt(mSymmetricEncryptionAlgorithm);
+ dest.writeLong(mSignatureMasterKeyId);
+ if (mSignatureSubKeyId != null) {
+ dest.writeInt(1);
+ dest.writeLong(mSignatureSubKeyId);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(mSignatureHashAlgorithm);
+ dest.writeString(mSignaturePassphrase);
+ dest.writeLong(mAdditionalEncryptId);
+ dest.writeByteArray(mNfcSignedHash);
+ if (mNfcCreationTimestamp != null) {
+ dest.writeInt(1);
+ dest.writeLong(mNfcCreationTimestamp.getTime());
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0);
+ dest.writeString(mCharset);
+ dest.writeInt(mCleartextSignature ? 1 : 0);
+ dest.writeInt(mDetachedSignature ? 1 : 0);
+
+ dest.writeTypedList(mInputUris);
+ dest.writeTypedList(mOutputUris);
+ dest.writeByteArray(mBytes);
+ }
+
+ public static final Creator<SignEncryptParcel> CREATOR = new Creator<SignEncryptParcel>() {
+ public SignEncryptParcel createFromParcel(final Parcel source) {
+ return new SignEncryptParcel(source);
+ }
+
+ public SignEncryptParcel[] newArray(final int size) {
+ return new SignEncryptParcel[size];
+ }
+ };
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 04fb955fa..681aff56d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -35,9 +35,9 @@ import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -445,7 +445,7 @@ public class UncachedKeyRing {
}
}
- ArrayList<String> processedUserIds = new ArrayList<String>();
+ ArrayList<String> processedUserIds = new ArrayList<>();
for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) {
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
@@ -470,7 +470,7 @@ public class UncachedKeyRing {
@SuppressWarnings("unchecked")
Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForID(rawUserId);
if (signaturesIt != null) {
- for (PGPSignature zert : new IterableIterator<PGPSignature>(signaturesIt)) {
+ for (PGPSignature zert : new IterableIterator<>(signaturesIt)) {
WrappedSignature cert = new WrappedSignature(zert);
long certId = cert.getKeyId();
@@ -635,7 +635,7 @@ public class UncachedKeyRing {
@SuppressWarnings("unchecked")
Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForUserAttribute(userAttribute);
if (signaturesIt != null) {
- for (PGPSignature zert : new IterableIterator<PGPSignature>(signaturesIt)) {
+ for (PGPSignature zert : new IterableIterator<>(signaturesIt)) {
WrappedSignature cert = new WrappedSignature(zert);
long certId = cert.getKeyId();
@@ -778,7 +778,7 @@ public class UncachedKeyRing {
}
// Keep track of ids we encountered so far
- Set<Long> knownIds = new HashSet<Long>();
+ Set<Long> knownIds = new HashSet<>();
// Process all keys
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(ring.getPublicKeys())) {
@@ -1042,7 +1042,7 @@ public class UncachedKeyRing {
}
// remember which certs we already added. this is cheaper than semantic deduplication
- Set<byte[]> certs = new TreeSet<byte[]>(new Comparator<byte[]>() {
+ Set<byte[]> certs = new TreeSet<>(new Comparator<byte[]>() {
public int compare(byte[] left, byte[] right) {
// check for length equality
if (left.length != right.length) {
@@ -1124,7 +1124,7 @@ public class UncachedKeyRing {
if (signaturesIt == null) {
continue;
}
- for (PGPSignature cert : new IterableIterator<PGPSignature>(signaturesIt)) {
+ for (PGPSignature cert : new IterableIterator<>(signaturesIt)) {
// Don't merge foreign stuff into secret keys
if (cert.getKeyID() != masterKeyId && isSecret()) {
continue;
@@ -1149,7 +1149,7 @@ public class UncachedKeyRing {
if (signaturesIt == null) {
continue;
}
- for (PGPSignature cert : new IterableIterator<PGPSignature>(signaturesIt)) {
+ for (PGPSignature cert : new IterableIterator<>(signaturesIt)) {
// Don't merge foreign stuff into secret keys
if (cert.getKeyID() != masterKeyId && isSecret()) {
continue;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
index 9e3528515..0173a1d83 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ECPublicBCPGKey;
import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
@@ -51,7 +50,7 @@ public class UncachedPublicKey {
}
/** The revocation signature is NOT checked here, so this may be false! */
- public boolean isRevoked() {
+ public boolean isMaybeRevoked() {
return mPublicKey.getSignaturesOfType(isMasterKey()
? PGPSignature.KEY_REVOCATION
: PGPSignature.SUBKEY_REVOCATION).hasNext();
@@ -61,25 +60,8 @@ public class UncachedPublicKey {
return mPublicKey.getCreationTime();
}
- public Date getExpiryTime() {
- long seconds = mPublicKey.getValidSeconds();
- if (seconds > Integer.MAX_VALUE) {
- Log.e(Constants.TAG, "error, expiry time too large");
- return null;
- }
- if (seconds == 0) {
- // no expiry
- return null;
- }
- Date creationDate = getCreationTime();
- Calendar calendar = GregorianCalendar.getInstance();
- calendar.setTime(creationDate);
- calendar.add(Calendar.SECOND, (int) seconds);
-
- return calendar.getTime();
- }
-
- public boolean isExpired() {
+ /** The revocation signature is NOT checked here, so this may be false! */
+ public boolean isMaybeExpired() {
Date creationDate = mPublicKey.getCreationTime();
Date expiryDate = mPublicKey.getValidSeconds() > 0
? new Date(creationDate.getTime() + mPublicKey.getValidSeconds() * 1000) : null;
@@ -136,7 +118,7 @@ public class UncachedPublicKey {
continue;
}
- for (PGPSignature sig : new IterableIterator<PGPSignature>(signaturesIt)) {
+ for (PGPSignature sig : new IterableIterator<>(signaturesIt)) {
try {
// if this is a revocation, this is not the user id
@@ -200,7 +182,7 @@ public class UncachedPublicKey {
}
public ArrayList<String> getUnorderedUserIds() {
- ArrayList<String> userIds = new ArrayList<String>();
+ ArrayList<String> userIds = new ArrayList<>();
for (byte[] rawUserId : new IterableIterator<byte[]>(mPublicKey.getRawUserIDs())) {
// use our decoding method
userIds.add(Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId));
@@ -209,7 +191,7 @@ public class UncachedPublicKey {
}
public ArrayList<byte[]> getUnorderedRawUserIds() {
- ArrayList<byte[]> userIds = new ArrayList<byte[]>();
+ ArrayList<byte[]> userIds = new ArrayList<>();
for (byte[] userId : new IterableIterator<byte[]>(mPublicKey.getRawUserIDs())) {
userIds.add(userId);
}
@@ -217,7 +199,7 @@ public class UncachedPublicKey {
}
public ArrayList<WrappedUserAttribute> getUnorderedUserAttributes() {
- ArrayList<WrappedUserAttribute> userAttributes = new ArrayList<WrappedUserAttribute>();
+ ArrayList<WrappedUserAttribute> userAttributes = new ArrayList<>();
for (PGPUserAttributeSubpacketVector userAttribute :
new IterableIterator<PGPUserAttributeSubpacketVector>(mPublicKey.getUserAttributes())) {
userAttributes.add(new WrappedUserAttribute(userAttribute));
@@ -305,28 +287,79 @@ public class UncachedPublicKey {
*
* Note that this method has package visiblity because it is used in test
* cases. Certificates of UncachedPublicKey instances can NOT be assumed to
- * be verified, so the result of this method should not be used in other
- * places!
+ * be verified or even by the correct key, so the result of this method
+ * should never be used in other places!
*/
@SuppressWarnings("unchecked")
Integer getKeyUsage() {
if (mCacheUsage == null) {
+ PGPSignature mostRecentSig = null;
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignatures())) {
if (mPublicKey.isMasterKey() && sig.getKeyID() != mPublicKey.getKeyID()) {
continue;
}
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+ switch (sig.getSignatureType()) {
+ case PGPSignature.DEFAULT_CERTIFICATION:
+ case PGPSignature.POSITIVE_CERTIFICATION:
+ case PGPSignature.CASUAL_CERTIFICATION:
+ case PGPSignature.NO_CERTIFICATION:
+ case PGPSignature.SUBKEY_BINDING:
+ break;
+ // if this is not one of the above types, don't care
+ default:
+ continue;
+ }
+
+ // If we have no sig yet, take the first we can get
+ if (mostRecentSig == null) {
+ mostRecentSig = sig;
+ continue;
+ }
+
+ // If the new sig is less recent, skip it
+ if (mostRecentSig.getCreationTime().after(sig.getCreationTime())) {
+ continue;
+ }
+
+ // Otherwise, note it down as the new "most recent" one
+ mostRecentSig = sig;
+ }
+
+ // Initialize to 0 as cached but empty value, if there is no sig (can't happen
+ // for canonicalized keyring), or there is no KEY_FLAGS packet in the sig
+ mCacheUsage = 0;
+ if (mostRecentSig != null) {
+ // If a mostRecentSig has been found, (cache and) return its flags
+ PGPSignatureSubpacketVector hashed = mostRecentSig.getHashedSubPackets();
if (hashed != null && hashed.getSubpacket(SignatureSubpacketTags.KEY_FLAGS) != null) {
- // init if at least one key flag subpacket has been found
- if (mCacheUsage == null) {
- mCacheUsage = 0;
- }
- mCacheUsage |= hashed.getKeyFlags();
+ mCacheUsage = hashed.getKeyFlags();
}
}
+
}
return mCacheUsage;
}
+ // this method relies on UNSAFE assumptions about the keyring, and should ONLY be used for
+ // TEST CASES!!
+ Date getUnsafeExpiryTimeForTesting () {
+ long valid = mPublicKey.getValidSeconds();
+
+ if (valid > Integer.MAX_VALUE) {
+ Log.e(Constants.TAG, "error, expiry time too large");
+ return null;
+ }
+ if (valid == 0) {
+ // no expiry
+ return null;
+ }
+ Date creationDate = getCreationTime();
+ Calendar calendar = GregorianCalendar.getInstance();
+ calendar.setTime(creationDate);
+ calendar.add(Calendar.SECOND, (int) valid);
+
+ return calendar.getTime();
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
index cb03970e2..c6fad1a73 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -36,7 +36,6 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
-import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
@@ -79,8 +78,12 @@ public class WrappedSignature {
return mSig.getCreationTime();
}
+ public long getKeyExpirySeconds() {
+ return mSig.getHashedSubPackets().getKeyExpirationTime();
+ }
+
public ArrayList<WrappedSignature> getEmbeddedSignatures() {
- ArrayList<WrappedSignature> sigs = new ArrayList<WrappedSignature>();
+ ArrayList<WrappedSignature> sigs = new ArrayList<>();
if (!mSig.hasSubpackets()) {
return sigs;
}
@@ -255,7 +258,7 @@ public class WrappedSignature {
}
public HashMap<String,String> getNotation() {
- HashMap<String,String> result = new HashMap<String,String>();
+ HashMap<String,String> result = new HashMap<>();
// If there is any notation data
if (mSig.getHashedSubPackets() != null
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java
index 370c3f89d..8f967499e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java
@@ -1,5 +1,8 @@
/*
+<<<<<<< HEAD
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+=======
+>>>>>>> development
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* This program is free software: you can redistribute it and/or modify
@@ -33,7 +36,6 @@ import java.io.IOException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Arrays;
public class WrappedUserAttribute implements Serializable {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index f4e00c36c..5856589c4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -85,6 +85,11 @@ public class KeychainContract {
String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name
}
+ interface ApiAppsAllowedKeysColumns {
+ String KEY_ID = "key_id"; // not a database id
+ String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name
+ }
+
public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME + ".provider";
private static final Uri BASE_CONTENT_URI_INTERNAL = Uri
@@ -106,6 +111,7 @@ public class KeychainContract {
public static final String BASE_API_APPS = "api_apps";
public static final String PATH_ACCOUNTS = "accounts";
+ public static final String PATH_ALLOWED_KEYS = "allowed_keys";
public static class KeyRings implements BaseColumns, KeysColumns, UserPacketsColumns {
public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID;
@@ -305,6 +311,22 @@ public class KeychainContract {
}
}
+ public static class ApiAllowedKeys implements ApiAppsAllowedKeysColumns, 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.org.sufficientlysecure.keychain.provider.api_apps.allowed_keys";
+
+ public static Uri buildBaseUri(String packageName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ALLOWED_KEYS)
+ .build();
+ }
+ }
+
public static class Certs implements CertsColumns, BaseColumns {
public static final String USER_ID = UserPacketsColumns.USER_ID;
public static final String SIGNER_UID = "signer_user_id";
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 5ce5eec17..b88cd6006 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -29,6 +29,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAllowedKeysColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
@@ -52,7 +53,7 @@ import java.io.IOException;
*/
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db";
- private static final int DATABASE_VERSION = 7;
+ private static final int DATABASE_VERSION = 8;
static Boolean apgHack = false;
private Context mContext;
@@ -64,6 +65,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
String CERTS = "certs";
String API_APPS = "api_apps";
String API_ACCOUNTS = "api_accounts";
+ String API_ALLOWED_KEYS = "api_allowed_keys";
}
private static final String CREATE_KEYRINGS_PUBLIC =
@@ -166,6 +168,18 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE"
+ ")";
+ private static final String CREATE_API_APPS_ALLOWED_KEYS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.API_ALLOWED_KEYS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + ApiAppsAllowedKeysColumns.KEY_ID + " INTEGER, "
+ + ApiAppsAllowedKeysColumns.PACKAGE_NAME + " TEXT NOT NULL, "
+
+ + "UNIQUE(" + ApiAppsAllowedKeysColumns.KEY_ID + ", "
+ + ApiAppsAllowedKeysColumns.PACKAGE_NAME + "), "
+ + "FOREIGN KEY(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") REFERENCES "
+ + Tables.API_APPS + "(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") ON DELETE CASCADE"
+ + ")";
+
KeychainDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
@@ -195,6 +209,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL(CREATE_CERTS);
db.execSQL(CREATE_API_APPS);
db.execSQL(CREATE_API_APPS_ACCOUNTS);
+ db.execSQL(CREATE_API_APPS_ALLOWED_KEYS);
}
@Override
@@ -243,6 +258,15 @@ public class KeychainDatabase extends SQLiteOpenHelper {
case 6:
db.execSQL("ALTER TABLE user_ids ADD COLUMN type INTEGER");
db.execSQL("ALTER TABLE user_ids ADD COLUMN attribute_data BLOB");
+ case 7:
+ // consolidate
+ case 8:
+ // new table for allowed key ids in API
+ try {
+ db.execSQL(CREATE_API_APPS_ALLOWED_KEYS);
+ } catch (Exception e) {
+ // never mind, the column probably already existed
+ }
}
// always do consolidate after upgrade
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index 29b9c5f9f..3aa156a8e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
@@ -63,9 +64,10 @@ public class KeychainProvider extends ContentProvider {
private static final int KEY_RING_CERTS_SPECIFIC = 206;
private static final int API_APPS = 301;
- 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 API_APPS_BY_PACKAGE_NAME = 302;
+ private static final int API_ACCOUNTS = 303;
+ private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 304;
+ private static final int API_ALLOWED_KEYS = 305;
private static final int KEY_RINGS_FIND_BY_EMAIL = 400;
private static final int KEY_RINGS_FIND_BY_SUBKEY = 401;
@@ -162,6 +164,8 @@ public class KeychainProvider extends ContentProvider {
*
* api_apps/_/accounts
* api_apps/_/accounts/_ (account name)
+ *
+ * api_apps/_/allowed_keys
* </pre>
*/
matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS);
@@ -172,6 +176,9 @@ public class KeychainProvider extends ContentProvider {
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ + KeychainContract.PATH_ALLOWED_KEYS, API_ALLOWED_KEYS);
+
return matcher;
}
@@ -223,6 +230,9 @@ public class KeychainProvider extends ContentProvider {
case API_ACCOUNTS_BY_ACCOUNT_NAME:
return ApiAccounts.CONTENT_ITEM_TYPE;
+ case API_ALLOWED_KEYS:
+ return ApiAllowedKeys.CONTENT_TYPE;
+
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
@@ -248,7 +258,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_UNIFIED:
case KEY_RINGS_FIND_BY_EMAIL:
case KEY_RINGS_FIND_BY_SUBKEY: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
projectionMap.put(KeyRings.KEY_ID, Tables.KEYS + "." + Keys.KEY_ID);
@@ -432,7 +442,7 @@ public class KeychainProvider extends ContentProvider {
}
case KEY_RING_KEYS: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(Keys._ID, Tables.KEYS + ".oid AS _id");
projectionMap.put(Keys.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
projectionMap.put(Keys.RANK, Tables.KEYS + "." + Keys.RANK);
@@ -460,7 +470,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_USER_IDS:
case KEY_RING_USER_IDS: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(UserPackets._ID, Tables.USER_PACKETS + ".oid AS _id");
projectionMap.put(UserPackets.MASTER_KEY_ID, Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID);
projectionMap.put(UserPackets.TYPE, Tables.USER_PACKETS + "." + UserPackets.TYPE);
@@ -507,7 +517,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_PUBLIC:
case KEY_RING_PUBLIC: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(KeyRingData._ID, Tables.KEY_RINGS_PUBLIC + ".oid AS _id");
projectionMap.put(KeyRingData.MASTER_KEY_ID, KeyRingData.MASTER_KEY_ID);
projectionMap.put(KeyRingData.KEY_RING_DATA, KeyRingData.KEY_RING_DATA);
@@ -525,7 +535,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_SECRET:
case KEY_RING_SECRET: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(KeyRingData._ID, Tables.KEY_RINGS_SECRET + ".oid AS _id");
projectionMap.put(KeyRingData.MASTER_KEY_ID, KeyRingData.MASTER_KEY_ID);
projectionMap.put(KeyRingData.KEY_RING_DATA, KeyRingData.KEY_RING_DATA);
@@ -543,7 +553,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RING_CERTS:
case KEY_RING_CERTS_SPECIFIC: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(Certs._ID, Tables.CERTS + ".oid AS " + Certs._ID);
projectionMap.put(Certs.MASTER_KEY_ID, Tables.CERTS + "." + Certs.MASTER_KEY_ID);
projectionMap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK);
@@ -614,6 +624,12 @@ public class KeychainProvider extends ContentProvider {
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
+ case API_ALLOWED_KEYS:
+ qb.setTables(Tables.API_ALLOWED_KEYS);
+ qb.appendWhere(Tables.API_ALLOWED_KEYS + "." + ApiAccounts.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+
+ break;
default:
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
@@ -701,7 +717,7 @@ public class KeychainProvider extends ContentProvider {
db.insertOrThrow(Tables.API_APPS, null, values);
break;
- case API_ACCOUNTS:
+ 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);
@@ -709,12 +725,21 @@ public class KeychainProvider extends ContentProvider {
db.insertOrThrow(Tables.API_ACCOUNTS, null, values);
break;
+ }
+ case API_ALLOWED_KEYS: {
+ // set foreign key automatically based on given uri
+ // e.g., api_apps/com.example.app/allowed_keys/
+ String packageName = uri.getPathSegments().get(1);
+ values.put(ApiAllowedKeys.PACKAGE_NAME, packageName);
+ db.insertOrThrow(Tables.API_ALLOWED_KEYS, null, values);
+ break;
+ }
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
- if(keyId != null) {
+ if (keyId != null) {
uri = KeyRings.buildGenericKeyRingUri(keyId);
rowUri = uri;
}
@@ -777,6 +802,10 @@ public class KeychainProvider extends ContentProvider {
count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, additionalSelection),
selectionArgs);
break;
+ case API_ALLOWED_KEYS:
+ count = db.delete(Tables.API_ALLOWED_KEYS, buildDefaultApiAllowedKeysSelection(uri, additionalSelection),
+ selectionArgs);
+ break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
@@ -869,4 +898,15 @@ public class KeychainProvider extends ContentProvider {
+ andSelection;
}
+ private String buildDefaultApiAllowedKeysSelection(Uri uri, String selection) {
+ String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1));
+
+ String andSelection = "";
+ if (!TextUtils.isEmpty(selection)) {
+ andSelection = " AND (" + selection + ")";
+ }
+
+ return ApiAllowedKeys.PACKAGE_NAME + "=" + packageName + andSelection;
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index c3990229d..8a9f36c03 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -37,6 +37,12 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.ImportExportOperation;
+import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
@@ -44,26 +50,27 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.operations.ImportExportOperation;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.remote.AppSettings;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableFileCache;
+import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
+import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.ProgressFixedScaler;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.Utf8Util;
@@ -170,7 +177,7 @@ public class ProviderHelper {
Cursor cursor = mContentResolver.query(uri, proj, selection, null, null);
try {
- HashMap<String, Object> result = new HashMap<String, Object>(proj.length);
+ HashMap<String, Object> result = new HashMap<>(proj.length);
if (cursor != null && cursor.moveToFirst()) {
int pos = 0;
for (String p : proj) {
@@ -221,7 +228,7 @@ public class ProviderHelper {
}, KeyRings.HAS_ANY_SECRET + " = 1", null, null);
try {
- LongSparseArray<CanonicalizedPublicKey> result = new LongSparseArray<CanonicalizedPublicKey>();
+ LongSparseArray<CanonicalizedPublicKey> result = new LongSparseArray<>();
if (cursor != null && cursor.moveToFirst()) do {
long masterKeyId = cursor.getLong(0);
@@ -350,7 +357,7 @@ public class ProviderHelper {
mIndent += 1;
// save all keys and userIds included in keyRing object in database
- operations = new ArrayList<ContentProviderOperation>();
+ operations = new ArrayList<>();
log(LogType.MSG_IP_INSERT_KEYRING);
{ // insert keyring
@@ -440,7 +447,7 @@ public class ProviderHelper {
// classify and order user ids. primary are moved to the front, revoked to the back,
// otherwise the order in the keyfile is preserved.
- List<UserPacketItem> uids = new ArrayList<UserPacketItem>();
+ List<UserPacketItem> uids = new ArrayList<>();
if (trustedKeys.size() == 0) {
log(LogType.MSG_IP_UID_CLASSIFYING_ZERO);
@@ -460,7 +467,7 @@ public class ProviderHelper {
log(LogType.MSG_IP_UID_PROCESSING, userId);
mIndent += 1;
// look through signatures for this specific key
- for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
+ for (WrappedSignature cert : new IterableIterator<>(
masterKey.getSignaturesForRawId(rawUserId))) {
long certId = cert.getKeyId();
// self signature
@@ -472,7 +479,7 @@ public class ProviderHelper {
item.selfCert = cert;
item.isPrimary = cert.isPrimaryUserId();
} else {
- item.isRevoked = true;
+ item.selfRevocation = cert;
log(LogType.MSG_IP_UID_REVOKED);
}
continue;
@@ -560,7 +567,7 @@ public class ProviderHelper {
}
mIndent += 1;
// look through signatures for this specific key
- for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
+ for (WrappedSignature cert : new IterableIterator<>(
masterKey.getSignaturesForUserAttribute(userAttribute))) {
long certId = cert.getKeyId();
// self signature
@@ -568,10 +575,11 @@ public class ProviderHelper {
// NOTE self-certificates are already verified during canonicalization,
// AND we know there is at most one cert plus at most one revocation
+ // AND the revocation only exists if there is no newer certification
if (!cert.isRevocation()) {
item.selfCert = cert;
} else {
- item.isRevoked = true;
+ item.selfRevocation = cert;
log(LogType.MSG_IP_UAT_REVOKED);
}
continue;
@@ -642,16 +650,21 @@ public class ProviderHelper {
for (int userIdRank = 0; userIdRank < uids.size(); userIdRank++) {
UserPacketItem item = uids.get(userIdRank);
operations.add(buildUserIdOperations(masterKeyId, item, userIdRank));
- if (item.selfCert != null) {
- // TODO get rid of "self verified" status? this cannot even happen anymore!
- operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfCert,
- selfCertsAreTrusted ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF));
+
+ if (item.selfCert == null) {
+ throw new AssertionError("User ids MUST be self-certified at this point!!");
}
- // don't bother with trusted certs if the uid is revoked, anyways
- if (item.isRevoked) {
+
+ if (item.selfRevocation != null) {
+ operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfRevocation,
+ Certs.VERIFIED_SELF));
+ // don't bother with trusted certs if the uid is revoked, anyways
continue;
}
+ operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfCert,
+ selfCertsAreTrusted ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF));
+
// iterate over signatures
for (int i = 0; i < item.trustedCerts.size() ; i++) {
WrappedSignature sig = item.trustedCerts.valueAt(i);
@@ -710,12 +723,17 @@ public class ProviderHelper {
String userId;
byte[] attributeData;
boolean isPrimary = false;
- boolean isRevoked = false;
WrappedSignature selfCert;
- LongSparseArray<WrappedSignature> trustedCerts = new LongSparseArray<WrappedSignature>();
+ WrappedSignature selfRevocation;
+ LongSparseArray<WrappedSignature> trustedCerts = new LongSparseArray<>();
@Override
public int compareTo(UserPacketItem o) {
+ // revoked keys always come last!
+ //noinspection DoubleNegation
+ if ( (selfRevocation != null) != (o.selfRevocation != null)) {
+ return selfRevocation != null ? 1 : -1;
+ }
// if one is a user id, but the other isn't, the user id always comes first.
// we compare for null values here, so != is the correct operator!
// noinspection NumberEquality
@@ -726,10 +744,6 @@ public class ProviderHelper {
if (isPrimary != o.isPrimary) {
return isPrimary ? -1 : 1;
}
- // revoked keys always come last!
- if (isRevoked != o.isRevoked) {
- return isRevoked ? 1 : -1;
- }
return 0;
}
}
@@ -1053,6 +1067,11 @@ public class ProviderHelper {
log.add(LogType.MSG_CON, indent);
indent += 1;
+ if (mConsolidateCritical) {
+ log.add(LogType.MSG_CON_RECURSIVE, indent);
+ return new ConsolidateResult(ConsolidateResult.RESULT_OK, log);
+ }
+
progress.setProgress(R.string.progress_con_saving, 0, 100);
// The consolidate operation can never be cancelled!
@@ -1075,7 +1094,7 @@ public class ProviderHelper {
// No keys existing might be a legitimate option, we write an empty file in that case
cursor.moveToFirst();
ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<ParcelableKeyRing>(mContext, "consolidate_secret.pcl");
+ new ParcelableFileCache<>(mContext, "consolidate_secret.pcl");
cache.writeCache(cursor.getCount(), new Iterator<ParcelableKeyRing>() {
ParcelableKeyRing ring;
@@ -1137,7 +1156,7 @@ public class ProviderHelper {
// No keys existing might be a legitimate option, we write an empty file in that case
cursor.moveToFirst();
ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<ParcelableKeyRing>(mContext, "consolidate_public.pcl");
+ new ParcelableFileCache<>(mContext, "consolidate_public.pcl");
cache.writeCache(cursor.getCount(), new Iterator<ParcelableKeyRing>() {
ParcelableKeyRing ring;
@@ -1219,13 +1238,11 @@ public class ProviderHelper {
log.add(LogType.MSG_CON_DB_CLEAR, indent);
mContentResolver.delete(KeyRings.buildUnifiedKeyRingsUri(), null, null);
- ParcelableFileCache<ParcelableKeyRing> cacheSecret =
- new ParcelableFileCache<ParcelableKeyRing>(mContext, "consolidate_secret.pcl");
- ParcelableFileCache<ParcelableKeyRing> cachePublic =
- new ParcelableFileCache<ParcelableKeyRing>(mContext, "consolidate_public.pcl");
+ ParcelableFileCache<ParcelableKeyRing> cacheSecret, cachePublic;
// Set flag that we have a cached consolidation here
try {
+ cacheSecret = new ParcelableFileCache<>(mContext, "consolidate_secret.pcl");
IteratorWithSize<ParcelableKeyRing> itSecrets = cacheSecret.readCache(false);
int numSecrets = itSecrets.getSize();
@@ -1253,6 +1270,7 @@ public class ProviderHelper {
try {
+ cachePublic = new ParcelableFileCache<>(mContext, "consolidate_public.pcl");
IteratorWithSize<ParcelableKeyRing> itPublics = cachePublic.readCache();
int numPublics = itPublics.getSize();
@@ -1308,7 +1326,6 @@ public class ProviderHelper {
progress.setProgress(100, 100);
log.add(LogType.MSG_CON_SUCCESS, indent);
- indent -= 1;
return new ConsolidateResult(ConsolidateResult.RESULT_OK, log);
@@ -1349,7 +1366,7 @@ public class ProviderHelper {
values.put(UserPackets.USER_ID, item.userId);
values.put(UserPackets.ATTRIBUTE_DATA, item.attributeData);
values.put(UserPackets.IS_PRIMARY, item.isPrimary);
- values.put(UserPackets.IS_REVOKED, item.isRevoked);
+ values.put(UserPackets.IS_REVOKED, item.selfRevocation != null);
values.put(UserPackets.RANK, rank);
Uri uri = UserPackets.buildUserIdsUri(masterKeyId);
@@ -1380,7 +1397,7 @@ public class ProviderHelper {
public ArrayList<String> getRegisteredApiApps() {
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
- ArrayList<String> packageNames = new ArrayList<String>();
+ ArrayList<String> packageNames = new ArrayList<>();
try {
if (cursor != null) {
int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
@@ -1485,7 +1502,7 @@ public class ProviderHelper {
}
public Set<Long> getAllKeyIdsForApp(Uri uri) {
- Set<Long> keyIds = new HashSet<Long>();
+ Set<Long> keyIds = new HashSet<>();
Cursor cursor = mContentResolver.query(uri, null, null, null, null);
try {
@@ -1504,8 +1521,41 @@ public class ProviderHelper {
return keyIds;
}
+ public Set<Long> getAllowedKeyIdsForApp(Uri uri) {
+ Set<Long> keyIds = new HashSet<>();
+
+ Cursor cursor = mContentResolver.query(uri, null, null, null, null);
+ try {
+ if (cursor != null) {
+ int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAllowedKeys.KEY_ID);
+ while (cursor.moveToNext()) {
+ keyIds.add(cursor.getLong(keyIdColumn));
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return keyIds;
+ }
+
+ public void saveAllowedKeyIdsForApp(Uri uri, Set<Long> allowedKeyIds)
+ throws RemoteException, OperationApplicationException {
+ // wipe whole table of allowed keys for this account
+ mContentResolver.delete(uri, null, null);
+
+ // re-insert allowed key ids
+ for (Long keyId : allowedKeyIds) {
+ ContentValues values = new ContentValues();
+ values.put(ApiAllowedKeys.KEY_ID, keyId);
+ mContentResolver.insert(uri, values);
+ }
+ }
+
public Set<String> getAllFingerprints(Uri uri) {
- Set<String> fingerprints = new HashSet<String>();
+ Set<String> fingerprints = new HashSet<>();
String[] projection = new String[]{KeyRings.FINGERPRINT};
Cursor cursor = mContentResolver.query(uri, projection, null, null, null);
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index a5813567a..1a65b1bee 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -26,35 +26,34 @@ import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import org.openintents.openpgp.IOpenPgpService;
-import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpError;
+import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
import org.spongycastle.util.encoders.Hex;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.ui.NfcActivity;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
+import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
+import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput;
+import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
-import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
+import org.sufficientlysecure.keychain.ui.NfcActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
@@ -85,9 +84,9 @@ public class OpenPgpService extends RemoteService {
boolean missingUserIdsCheck = false;
boolean duplicateUserIdsCheck = false;
- ArrayList<Long> keyIds = new ArrayList<Long>();
- ArrayList<String> missingUserIds = new ArrayList<String>();
- ArrayList<String> duplicateUserIds = new ArrayList<String>();
+ ArrayList<Long> keyIds = new ArrayList<>();
+ ArrayList<String> missingUserIds = new ArrayList<>();
+ ArrayList<String> duplicateUserIds = new ArrayList<>();
if (!noUserIdsCheck) {
for (String email : encryptionUserIds) {
// try to find the key for this specific email
@@ -222,9 +221,12 @@ public class OpenPgpService extends RemoteService {
}
private Intent signImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AccountSettings accSettings) {
+ ParcelFileDescriptor output, AccountSettings accSettings,
+ boolean cleartextSign) {
+ InputStream is = null;
+ OutputStream os = null;
try {
- boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
if (nfcSignedHash != null) {
@@ -242,91 +244,55 @@ public class OpenPgpService extends RemoteService {
}
// Get Input- and OutputStream from ParcelFileDescriptor
- InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
- OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
- try {
- long inputLength = is.available();
- InputData inputData = new InputData(is, inputLength);
-
- // Find the appropriate subkey to sign with
- long sigSubKeyId;
- try {
- CachedPublicKeyRing signingRing =
- new ProviderHelper(this).getCachedPublicKeyRing(accSettings.getKeyId());
- sigSubKeyId = signingRing.getSecretSignId();
- } catch (PgpKeyNotFoundException e) {
- // secret key that is set for this account is deleted?
- // show account config again!
- return getCreateAccountIntent(data, getAccountName(data));
- }
-
- // get passphrase from cache, if key has "no" passphrase, this returns an empty String
- String passphrase;
- if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
- passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
+ is = new ParcelFileDescriptor.AutoCloseInputStream(input);
+ if (cleartextSign) {
+ // output stream only needed for cleartext signatures,
+ // detached signatures are returned as extra
+ os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
+ }
+ long inputLength = is.available();
+ InputData inputData = new InputData(is, inputLength);
+
+ // sign-only
+ PgpSignEncryptInput pseInput = new PgpSignEncryptInput()
+ .setEnableAsciiArmorOutput(asciiArmor)
+ .setCleartextSignature(cleartextSign)
+ .setDetachedSignature(!cleartextSign)
+ .setVersionHeader(PgpHelper.getVersionForHeader(this))
+ .setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
+ .setSignatureMasterKeyId(accSettings.getKeyId())
+ .setNfcState(nfcSignedHash, nfcCreationDate);
+
+ // execute PGP operation!
+ PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
+ PgpSignEncryptResult pgpResult = pse.execute(pseInput, inputData, os);
+
+ if (pgpResult.isPending()) {
+ if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
+ PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) {
+ return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
+ } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) ==
+ PgpSignEncryptResult.RESULT_PENDING_NFC) {
+ // return PendingIntent to execute NFC activity
+ // pass through the signature creation timestamp to be used again on second execution
+ // of PgpSignEncrypt when we have the signed hash!
+ data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
+ return getNfcSignIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
} else {
- try {
- passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
- accSettings.getKeyId(), sigSubKeyId);
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- // should happen earlier, but return again here if it happens
- return getCreateAccountIntent(data, getAccountName(data));
- }
+ throw new PgpGeneralException(
+ "Encountered unhandled type of pending action not supported by API!");
}
- if (passphrase == null) {
- // get PendingIntent for passphrase input, add it to given params and return to client
- return getPassphraseIntent(data, sigSubKeyId);
- }
-
- // sign-only
- PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
- this, new ProviderHelper(getContext()), null,
- inputData, os
- );
- builder.setEnableAsciiArmorOutput(asciiArmor)
- .setVersionHeader(PgpHelper.getVersionForHeader(this))
- .setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
- .setSignatureMasterKeyId(accSettings.getKeyId())
- .setSignatureSubKeyId(sigSubKeyId)
- .setSignaturePassphrase(passphrase)
- .setNfcState(nfcSignedHash, nfcCreationDate);
-
- // TODO: currently always assume cleartext input, no sign-only of binary currently!
- builder.setCleartextInput(true);
-
- // execute PGP operation!
- SignEncryptResult pgpResult = builder.build().execute();
-
- if (pgpResult.isPending()) {
- if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
- SignEncryptResult.RESULT_PENDING_PASSPHRASE) {
- return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
- } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) ==
- SignEncryptResult.RESULT_PENDING_NFC) {
- // return PendingIntent to execute NFC activity
- // pass through the signature creation timestamp to be used again on second execution
- // of PgpSignEncrypt when we have the signed hash!
- data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
- return getNfcSignIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
- } else {
- throw new PgpGeneralException(
- "Encountered unhandled type of pending action not supported by API!");
- }
- } else if (pgpResult.success()) {
- // see end of method
- } else {
- LogEntryParcel errorMsg = pgpResult.getLog().getLast();
- throw new Exception(getString(errorMsg.mType.getMsgId()));
+ } else if (pgpResult.success()) {
+ Intent result = new Intent();
+ if (!cleartextSign) {
+ result.putExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE, pgpResult.getDetachedSignature());
}
-
- } finally {
- is.close();
- os.close();
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
+ return result;
+ } else {
+ LogEntryParcel errorMsg = pgpResult.getLog().getLast();
+ throw new Exception(getString(errorMsg.mType.getMsgId()));
}
-
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
- return result;
} catch (Exception e) {
Log.d(Constants.TAG, "signImpl", e);
Intent result = new Intent();
@@ -334,12 +300,29 @@ public class OpenPgpService extends RemoteService {
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException when closing InputStream", e);
+ }
+ }
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException when closing OutputStream", e);
+ }
+ }
}
}
private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input,
ParcelFileDescriptor output, AccountSettings accSettings,
boolean sign) {
+ InputStream is = null;
+ OutputStream os = null;
try {
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
String originalFilename = data.getStringExtra(OpenPgpApi.EXTRA_ORIGINAL_FILENAME);
@@ -365,99 +348,65 @@ public class OpenPgpService extends RemoteService {
// build InputData and write into OutputStream
// Get Input- and OutputStream from ParcelFileDescriptor
- InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
- OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
- try {
- long inputLength = is.available();
- InputData inputData = new InputData(is, inputLength);
-
- PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
- this, new ProviderHelper(getContext()), null, inputData, os
- );
- builder.setEnableAsciiArmorOutput(asciiArmor)
- .setVersionHeader(PgpHelper.getVersionForHeader(this))
- .setCompressionId(accSettings.getCompression())
- .setSymmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm())
- .setEncryptionMasterKeyIds(keyIds)
- .setFailOnMissingEncryptionKeyIds(true)
- .setOriginalFilename(originalFilename)
- .setAdditionalEncryptId(accSettings.getKeyId()); // add acc key for encryption
-
- if (sign) {
-
- // Find the appropriate subkey to sign with
- long sigSubKeyId;
- try {
- CachedPublicKeyRing signingRing =
- new ProviderHelper(this).getCachedPublicKeyRing(accSettings.getKeyId());
- sigSubKeyId = signingRing.getSecretSignId();
- } catch (PgpKeyNotFoundException e) {
- // secret key that is set for this account is deleted?
- // show account config again!
- return getCreateAccountIntent(data, getAccountName(data));
- }
-
- String passphrase;
- if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
- passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
- } else {
- passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
- accSettings.getKeyId(), sigSubKeyId);
- }
- if (passphrase == null) {
- // get PendingIntent for passphrase input, add it to given params and return to client
- return getPassphraseIntent(data, sigSubKeyId);
- }
-
- byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
- // carefully: only set if timestamp exists
- Date nfcCreationDate = null;
- long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
- if (nfcCreationTimestamp != -1) {
- nfcCreationDate = new Date(nfcCreationTimestamp);
- }
-
- // sign and encrypt
- builder.setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
- .setSignatureMasterKeyId(accSettings.getKeyId())
- .setSignatureSubKeyId(sigSubKeyId)
- .setSignaturePassphrase(passphrase)
- .setNfcState(nfcSignedHash, nfcCreationDate);
+ is = new ParcelFileDescriptor.AutoCloseInputStream(input);
+ os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
+
+ long inputLength = is.available();
+ InputData inputData = new InputData(is, inputLength, originalFilename);
+
+ PgpSignEncryptInput pseInput = new PgpSignEncryptInput();
+ pseInput.setEnableAsciiArmorOutput(asciiArmor)
+ .setVersionHeader(PgpHelper.getVersionForHeader(this))
+ .setCompressionId(accSettings.getCompression())
+ .setSymmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm())
+ .setEncryptionMasterKeyIds(keyIds)
+ .setFailOnMissingEncryptionKeyIds(true)
+ .setAdditionalEncryptId(accSettings.getKeyId()); // add acc key for encryption
+
+ if (sign) {
+
+ byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
+ // carefully: only set if timestamp exists
+ Date nfcCreationDate = null;
+ long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
+ if (nfcCreationTimestamp != -1) {
+ nfcCreationDate = new Date(nfcCreationTimestamp);
}
- // execute PGP operation!
- SignEncryptResult pgpResult = builder.build().execute();
-
- if (pgpResult.isPending()) {
- if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
- SignEncryptResult.RESULT_PENDING_PASSPHRASE) {
- return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
- } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) ==
- SignEncryptResult.RESULT_PENDING_NFC) {
- // return PendingIntent to execute NFC activity
- // pass through the signature creation timestamp to be used again on second execution
- // of PgpSignEncrypt when we have the signed hash!
- data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
- return getNfcSignIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
- } else {
- throw new PgpGeneralException(
- "Encountered unhandled type of pending action not supported by API!");
- }
- } else if (pgpResult.success()) {
- // see end of method
+ // sign and encrypt
+ pseInput.setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
+ .setSignatureMasterKeyId(accSettings.getKeyId())
+ .setNfcState(nfcSignedHash, nfcCreationDate);
+ }
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
+
+ // execute PGP operation!
+ PgpSignEncryptResult pgpResult = op.execute(pseInput, inputData, os);
+
+ if (pgpResult.isPending()) {
+ if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
+ PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) {
+ return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
+ } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) ==
+ PgpSignEncryptResult.RESULT_PENDING_NFC) {
+ // return PendingIntent to execute NFC activity
+ // pass through the signature creation timestamp to be used again on second execution
+ // of PgpSignEncrypt when we have the signed hash!
+ data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
+ return getNfcSignIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
} else {
- LogEntryParcel errorMsg = pgpResult.getLog().getLast();
- throw new Exception(getString(errorMsg.mType.getMsgId()));
+ throw new PgpGeneralException(
+ "Encountered unhandled type of pending action not supported by API!");
}
-
- } finally {
- is.close();
- os.close();
+ } else if (pgpResult.success()) {
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
+ return result;
+ } else {
+ LogEntryParcel errorMsg = pgpResult.getLog().getLast();
+ throw new Exception(getString(errorMsg.mType.getMsgId()));
}
-
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
- return result;
} catch (Exception e) {
Log.d(Constants.TAG, "encryptAndSignImpl", e);
Intent result = new Intent();
@@ -465,111 +414,129 @@ public class OpenPgpService extends RemoteService {
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException when closing InputStream", e);
+ }
+ }
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException when closing OutputStream", e);
+ }
+ }
}
}
private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input,
ParcelFileDescriptor output, Set<Long> allowedKeyIds,
boolean decryptMetadataOnly) {
+ InputStream is = null;
+ OutputStream os = null;
try {
// Get Input- and OutputStream from ParcelFileDescriptor
- InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
+ is = new ParcelFileDescriptor.AutoCloseInputStream(input);
- OutputStream os;
- if (decryptMetadataOnly) {
+ // output is optional, e.g., for verifying detached signatures
+ if (decryptMetadataOnly || output == null) {
os = null;
} else {
os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
}
- Intent result = new Intent();
- try {
- String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
- long inputLength = is.available();
- InputData inputData = new InputData(is, inputLength);
-
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(getContext()), null, inputData, os
- );
-
- byte[] nfcDecryptedSessionKey = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DECRYPTED_SESSION_KEY);
-
- // allow only private keys associated with accounts of this app
- // no support for symmetric encryption
- builder.setPassphrase(passphrase)
- .setAllowSymmetricDecryption(false)
- .setAllowedKeyIds(allowedKeyIds)
- .setDecryptMetadataOnly(decryptMetadataOnly)
- .setNfcState(nfcDecryptedSessionKey);
-
- // TODO: currently does not support binary signed-only content
- DecryptVerifyResult pgpResult = builder.build().execute();
-
- if (pgpResult.isPending()) {
- if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) ==
- DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) {
- return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
- } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) ==
- DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) {
- throw new PgpGeneralException(
- "Decryption of symmetric content not supported by API!");
- } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) ==
- DecryptVerifyResult.RESULT_PENDING_NFC) {
- return getNfcDecryptIntent(
- data, pgpResult.getNfcSubKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcEncryptedSessionKey());
- } else {
- throw new PgpGeneralException(
- "Encountered unhandled type of pending action not supported by API!");
- }
- } else if (pgpResult.success()) {
-
- OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult();
- if (signatureResult != null) {
- result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
-
- if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) {
- // SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED have been added in version 5
- if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED
- || signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED) {
- signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
- }
+ String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
+ long inputLength = is.available();
+ InputData inputData = new InputData(is, inputLength);
+
+ PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
+ this, new ProviderHelper(getContext()), null, inputData, os
+ );
+
+ byte[] nfcDecryptedSessionKey = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DECRYPTED_SESSION_KEY);
+
+ byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE);
+
+ // allow only private keys associated with accounts of this app
+ // no support for symmetric encryption
+ builder.setPassphrase(passphrase)
+ .setAllowSymmetricDecryption(false)
+ .setAllowedKeyIds(allowedKeyIds)
+ .setDecryptMetadataOnly(decryptMetadataOnly)
+ .setNfcState(nfcDecryptedSessionKey)
+ .setDetachedSignature(detachedSignature);
+
+ DecryptVerifyResult pgpResult = builder.build().execute();
+
+ if (pgpResult.isPending()) {
+ if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) ==
+ DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) {
+ return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
+ } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) ==
+ DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) {
+ throw new PgpGeneralException(
+ "Decryption of symmetric content not supported by API!");
+ } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) ==
+ DecryptVerifyResult.RESULT_PENDING_NFC) {
+ return getNfcDecryptIntent(
+ data, pgpResult.getNfcSubKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcEncryptedSessionKey());
+ } else {
+ throw new PgpGeneralException(
+ "Encountered unhandled type of pending action not supported by API!");
+ }
+ } else if (pgpResult.success()) {
+ Intent result = new Intent();
+
+ OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult();
+ if (signatureResult != null) {
+ result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
+
+ if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) {
+ // SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED have been added in version 5
+ if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED
+ || signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED) {
+ signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
}
+ }
- if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) {
- // If signature is unknown we return an _additional_ PendingIntent
- // to retrieve the missing key
- Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE);
- intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId());
- intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
+ if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) {
+ // If signature is unknown we return an _additional_ PendingIntent
+ // to retrieve the missing key
+ Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE);
+ intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId());
+ intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
- intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
- result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
- }
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
}
+ }
- if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) {
- OpenPgpMetadata metadata = pgpResult.getDecryptMetadata();
- if (metadata != null) {
- result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
- }
+ if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) {
+ OpenPgpMetadata metadata = pgpResult.getDecryptMetadata();
+ if (metadata != null) {
+ result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
}
- } else {
- LogEntryParcel errorMsg = pgpResult.getLog().getLast();
- throw new Exception(getString(errorMsg.mType.getMsgId()));
}
- } finally {
- is.close();
- if (os != null) {
- os.close();
+
+ String charset = pgpResult.getCharset();
+ if (charset != null) {
+ result.putExtra(OpenPgpApi.RESULT_CHARSET, charset);
}
+
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
+ return result;
+ } else {
+ LogEntryParcel errorMsg = pgpResult.getLog().getLast();
+ throw new Exception(getString(errorMsg.mType.getMsgId()));
}
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
- return result;
} catch (Exception e) {
Log.d(Constants.TAG, "decryptAndVerifyImpl", e);
Intent result = new Intent();
@@ -577,6 +544,21 @@ public class OpenPgpService extends RemoteService {
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException when closing InputStream", e);
+ }
+ }
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException when closing OutputStream", e);
+ }
+ }
}
}
@@ -668,15 +650,16 @@ public class OpenPgpService extends RemoteService {
// version code is required and needs to correspond to version code of service!
// History of versions in org.openintents.openpgp.util.OpenPgpApi
- // we support 3, 4, 5
+ // we support 3, 4, 5, 6
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 3
&& data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 4
- && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 5) {
+ && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 5
+ && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 6) {
Intent result = new Intent();
OpenPgpError error = new OpenPgpError
(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!\n"
+ "used API version: " + data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) + "\n"
- + "supported API versions: 3, 4");
+ + "supported API versions: 3, 4, 5, 6");
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
@@ -706,42 +689,66 @@ public class OpenPgpService extends RemoteService {
@Override
public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) {
- Intent errorResult = checkRequirements(data);
- if (errorResult != null) {
- return errorResult;
- }
+ try {
+ Intent errorResult = checkRequirements(data);
+ if (errorResult != null) {
+ return errorResult;
+ }
- String accName = getAccountName(data);
- final AccountSettings accSettings = getAccSettings(accName);
- if (accSettings == null) {
- return getCreateAccountIntent(data, accName);
- }
+ String accName = getAccountName(data);
+ 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, accSettings);
- } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, accSettings, false);
- } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, accSettings, true);
- } else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
- String currentPkg = getCurrentCallingPackage();
- Set<Long> allowedKeyIds =
- mProviderHelper.getAllKeyIdsForApp(
- ApiAccounts.buildBaseUri(currentPkg));
- return decryptAndVerifyImpl(data, input, output, allowedKeyIds, false);
- } else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) {
- String currentPkg = getCurrentCallingPackage();
- Set<Long> allowedKeyIds =
- mProviderHelper.getAllKeyIdsForApp(
- ApiAccounts.buildBaseUri(currentPkg));
- return decryptAndVerifyImpl(data, input, output, allowedKeyIds, true);
- } else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
- return getKeyImpl(data);
- } else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
- return getKeyIdsImpl(data);
- } else {
- return null;
+ String action = data.getAction();
+ if (OpenPgpApi.ACTION_CLEARTEXT_SIGN.equals(action)) {
+ return signImpl(data, input, output, accSettings, true);
+ } else if (OpenPgpApi.ACTION_SIGN.equals(action)) {
+ // DEPRECATED: same as ACTION_CLEARTEXT_SIGN
+ Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!");
+ return signImpl(data, input, output, accSettings, true);
+ } else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(action)) {
+ return signImpl(data, input, output, accSettings, false);
+ } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
+ return encryptAndSignImpl(data, input, output, accSettings, false);
+ } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
+ return encryptAndSignImpl(data, input, output, accSettings, true);
+ } else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
+ String currentPkg = getCurrentCallingPackage();
+ Set<Long> allowedKeyIds =
+ mProviderHelper.getAllKeyIdsForApp(
+ ApiAccounts.buildBaseUri(currentPkg));
+ return decryptAndVerifyImpl(data, input, output, allowedKeyIds, false);
+ } else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) {
+ String currentPkg = getCurrentCallingPackage();
+ Set<Long> allowedKeyIds =
+ mProviderHelper.getAllKeyIdsForApp(
+ ApiAccounts.buildBaseUri(currentPkg));
+ return decryptAndVerifyImpl(data, input, output, allowedKeyIds, true);
+ } else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
+ return getKeyImpl(data);
+ } else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
+ return getKeyIdsImpl(data);
+ } else {
+ return null;
+ }
+ } finally {
+ // always close input and output file descriptors even in error cases
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException when closing input ParcelFileDescriptor", e);
+ }
+ }
+ if (output != null) {
+ try {
+ output.close();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException when closing output ParcelFileDescriptor", e);
+ }
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
index e71b52ccd..672f59285 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
@@ -27,7 +27,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
-import android.text.TextUtils;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
@@ -216,9 +215,7 @@ public abstract class RemoteService extends Service {
String[] callingPackages = getPackageManager().getPackagesForUid(uid);
// is calling package allowed to use this service?
- for (int i = 0; i < callingPackages.length; i++) {
- String currentPkg = callingPackages[i];
-
+ for (String currentPkg : callingPackages) {
if (isPackageAllowed(currentPkg)) {
return true;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
index 0e9678980..4d6df24d2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
@@ -20,22 +20,21 @@ package org.sufficientlysecure.keychain.remote.ui;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.util.ActionBarHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.ui.BaseActivity;
import org.sufficientlysecure.keychain.util.Log;
-public class AccountSettingsActivity extends ActionBarActivity {
+public class AccountSettingsActivity extends BaseActivity {
private Uri mAccountUri;
private AccountSettingsFragment mAccountSettingsFragment;
@@ -45,17 +44,20 @@ public class AccountSettingsActivity extends ActionBarActivity {
super.onCreate(savedInstanceState);
// Inflate a "Done" custom action bar
- ActionBarHelper.setOneButtonView(getSupportActionBar(),
- R.string.api_settings_save, R.drawable.ic_action_done,
+ setFullScreenDialogDoneClose(R.string.api_settings_save,
new View.OnClickListener() {
@Override
public void onClick(View v) {
- // "Done"
save();
}
+ },
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
});
-
- setContentView(R.layout.api_account_settings_activity);
+ setTitle(null);
mAccountSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
R.id.api_account_settings_fragment);
@@ -73,6 +75,11 @@ public class AccountSettingsActivity extends ActionBarActivity {
}
@Override
+ protected void initLayout() {
+ setContentView(R.layout.api_account_settings_activity);
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.api_account_settings, menu);
@@ -85,9 +92,6 @@ public class AccountSettingsActivity extends ActionBarActivity {
case R.id.menu_account_settings_delete:
deleteAccount();
return true;
- case R.id.menu_account_settings_cancel:
- finish();
- return true;
}
return super.onOptionsItemSelected(item);
}
@@ -125,9 +129,4 @@ public class AccountSettingsActivity extends ActionBarActivity {
}
}
- @Override
- public void onBackPressed() {
- save();
- super.onBackPressed();
- }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
index a3027f728..a5bc05ba8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -31,10 +31,10 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
index 56e3b22e2..407480c98 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
@@ -17,29 +17,54 @@
package org.sufficientlysecure.keychain.remote.ui;
+import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
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 android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.getbase.floatingactionbutton.FloatingActionButton;
+
+import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AppSettings;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.ui.BaseActivity;
+import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.AdvancedAppSettingsDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
-public class AppSettingsActivity extends ActionBarActivity {
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class AppSettingsActivity extends BaseActivity {
private Uri mAppUri;
- private AppSettingsFragment mSettingsFragment;
+ private AppSettingsAllowedKeysListFragment mAllowedKeysFragment;
+
+ private TextView mAppNameView;
+ private ImageView mAppIconView;
+ private TextView mPackageName;
+ private TextView mPackageSignature;
+
+ private FloatingActionButton mStartFab;
+
+ // deprecated API
private AccountsListFragment mAccountsListFragment;
+ private TextView mAccountsLabel;
+
// model
AppSettings mAppSettings;
@@ -48,16 +73,27 @@ public class AppSettingsActivity extends ActionBarActivity {
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);
+ mAccountsLabel = (TextView) findViewById(R.id.api_accounts_label);
+ mAppNameView = (TextView) findViewById(R.id.api_app_settings_app_name);
+ mAppIconView = (ImageView) findViewById(R.id.api_app_settings_app_icon);
+ mPackageName = (TextView) findViewById(R.id.api_app_settings_package_name);
+ mPackageSignature = (TextView) findViewById(R.id.api_app_settings_package_signature);
+ mStartFab = (FloatingActionButton) findViewById(R.id.fab);
- setContentView(R.layout.api_app_settings_activity);
+ mStartFab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startApp();
+ }
+ });
- mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_app_settings_fragment);
+ setFullScreenDialogClose(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ cancel();
+ }
+ });
+ setTitle(null);
Intent intent = getIntent();
mAppUri = intent.getData();
@@ -71,6 +107,22 @@ public class AppSettingsActivity extends ActionBarActivity {
}
}
+ private void save() {
+ mAllowedKeysFragment.saveAllowedKeys();
+ setResult(Activity.RESULT_OK);
+ finish();
+ }
+
+ private void cancel() {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ }
+
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.api_app_settings_activity);
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
@@ -81,16 +133,40 @@ public class AppSettingsActivity extends ActionBarActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_api_settings_revoke:
+ case R.id.menu_api_save: {
+ save();
+ return true;
+ }
+ case R.id.menu_api_settings_revoke: {
revokeAccess();
return true;
- case R.id.menu_api_settings_start:
- startApp();
+ }
+ case R.id.menu_api_settings_advanced: {
+ showAdvancedInfo();
return true;
+ }
}
return super.onOptionsItemSelected(item);
}
+ private void showAdvancedInfo() {
+ String signature = null;
+ // advanced info: package signature SHA-256
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(mAppSettings.getPackageSignature());
+ byte[] digest = md.digest();
+ signature = new String(Hex.encode(digest));
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(Constants.TAG, "Should not happen!", e);
+ }
+
+ AdvancedAppSettingsDialogFragment dialogFragment =
+ AdvancedAppSettingsDialogFragment.newInstance(mAppSettings.getPackageName(), signature);
+
+ dialogFragment.show(getSupportFragmentManager(), "advancedDialog");
+ }
+
private void startApp() {
Intent i;
PackageManager manager = getPackageManager();
@@ -109,25 +185,31 @@ public class AppSettingsActivity extends ActionBarActivity {
private void loadData(Bundle savedInstanceState, Uri appUri) {
mAppSettings = new ProviderHelper(this).getApiAppSettings(appUri);
- mSettingsFragment.setAppSettings(mAppSettings);
+ // get application name and icon from package manager
String appName;
- PackageManager pm = getPackageManager();
+ Drawable appIcon = null;
+ PackageManager pm = getApplicationContext().getPackageManager();
try {
ApplicationInfo ai = pm.getApplicationInfo(mAppSettings.getPackageName(), 0);
+
appName = (String) pm.getApplicationLabel(ai);
+ appIcon = pm.getApplicationIcon(ai);
} catch (PackageManager.NameNotFoundException e) {
// fallback
appName = mAppSettings.getPackageName();
}
- setTitle(appName);
+ mAppNameView.setText(appName);
+ mAppIconView.setImageDrawable(appIcon);
Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build();
Log.d(Constants.TAG, "accountsUri: " + accountsUri);
- startListFragment(savedInstanceState, accountsUri);
+ Uri allowedKeysUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ALLOWED_KEYS).build();
+ Log.d(Constants.TAG, "allowedKeysUri: " + allowedKeysUri);
+ startListFragments(savedInstanceState, accountsUri, allowedKeysUri);
}
- private void startListFragment(Bundle savedInstanceState, Uri dataUri) {
+ private void startListFragments(Bundle savedInstanceState, Uri accountsUri, Uri allowedKeysUri) {
// 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.
@@ -135,13 +217,23 @@ public class AppSettingsActivity extends ActionBarActivity {
return;
}
- // Create an instance of the fragment
- mAccountsListFragment = AccountsListFragment.newInstance(dataUri);
+ // show accounts only if available (deprecated API)
+ Cursor cursor = getContentResolver().query(accountsUri, null, null, null, null);
+ if (cursor.moveToFirst()) {
+ mAccountsLabel.setVisibility(View.VISIBLE);
+ mAccountsListFragment = AccountsListFragment.newInstance(accountsUri);
+ // Create an instance of the fragments
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.api_accounts_list_fragment, mAccountsListFragment)
+ .commitAllowingStateLoss();
+ }
+ // Create an instance of the fragments
+ mAllowedKeysFragment = AppSettingsAllowedKeysListFragment.newInstance(allowedKeysUri);
// 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)
+ .replace(R.id.api_allowed_keys_list_fragment, mAllowedKeysFragment)
.commitAllowingStateLoss();
// do it immediately!
getSupportFragmentManager().executePendingTransactions();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java
new file mode 100644
index 000000000..13b242a5e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2015 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.OperationApplicationException;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+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.ViewGroup;
+import android.widget.ListView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
+import org.sufficientlysecure.keychain.ui.widget.FixedListView;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround implements LoaderManager.LoaderCallbacks<Cursor> {
+ private static final String ARG_DATA_URI = "uri";
+
+ private SelectKeyCursorAdapter mAdapter;
+ private Set<Long> mSelectedMasterKeyIds;
+ private ProviderHelper mProviderHelper;
+
+ private Uri mDataUri;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static AppSettingsAllowedKeysListFragment newInstance(Uri dataUri) {
+ AppSettingsAllowedKeysListFragment frag = new AppSettingsAllowedKeysListFragment();
+ Bundle args = new Bundle();
+
+ args.putParcelable(ARG_DATA_URI, dataUri);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mProviderHelper = new ProviderHelper(getActivity());
+ }
+
+ @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;
+ }
+
+ /**
+ * Define Adapter and Loader on create of Activity
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mDataUri = getArguments().getParcelable(ARG_DATA_URI);
+
+ getListView().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 SecretKeyCursorAdapter(getActivity(), null, 0, getListView());
+
+ setListAdapter(mAdapter);
+
+ // Start out with a progress indicator.
+ setListShown(false);
+
+ mSelectedMasterKeyIds = mProviderHelper.getAllKeyIdsForApp(mDataUri);
+ Log.d(Constants.TAG, "allowed: " + mSelectedMasterKeyIds.toString());
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ /**
+ * Selects items based on master key ids in list view
+ *
+ * @param masterKeyIds
+ */
+ private void preselectMasterKeyIds(Set<Long> masterKeyIds) {
+ for (int i = 0; i < getListView().getCount(); ++i) {
+ long listKeyId = mAdapter.getMasterKeyId(i);
+ for (long keyId : masterKeyIds) {
+ if (listKeyId == keyId) {
+ getListView().setItemChecked(i, true);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns all selected master key ids
+ *
+ * @return
+ */
+ public Set<Long> getSelectedMasterKeyIds() {
+ // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key
+ // ids!
+ Set<Long> keyIds = new HashSet<>();
+ for (int i = 0; i < getListView().getCount(); ++i) {
+ if (getListView().isItemChecked(i)) {
+ keyIds.add(mAdapter.getMasterKeyId(i));
+ }
+ }
+
+ return keyIds;
+ }
+
+ /**
+ * Returns all selected user ids
+ *
+ * @return
+ */
+ public String[] getSelectedUserIds() {
+ Vector<String> userIds = new Vector<>();
+ for (int i = 0; i < getListView().getCount(); ++i) {
+ if (getListView().isItemChecked(i)) {
+ userIds.add(mAdapter.getUserId(i));
+ }
+ }
+
+ // make empty array to not return null
+ String userIdArray[] = new String[0];
+ return userIds.toArray(userIdArray);
+ }
+
+ public void saveAllowedKeys() {
+ try {
+ mProviderHelper.saveAllowedKeyIdsForApp(mDataUri, getSelectedMasterKeyIds());
+ } catch (RemoteException | OperationApplicationException e) {
+ Log.e(Constants.TAG, "Problem saving allowed key ids!", e);
+ }
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
+
+ // These are the rows that we will retrieve.
+ String[] projection = new String[]{
+ KeyRings._ID,
+ KeyRings.MASTER_KEY_ID,
+ KeyRings.USER_ID,
+ KeyRings.IS_EXPIRED,
+ KeyRings.IS_REVOKED,
+ KeyRings.HAS_ENCRYPT,
+ KeyRings.VERIFIED,
+ KeyRings.HAS_ANY_SECRET,
+ };
+
+ String inMasterKeyList = null;
+ if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.size() > 0) {
+ inMasterKeyList = Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN (";
+ Iterator iter = mSelectedMasterKeyIds.iterator();
+ while (iter.hasNext()) {
+ inMasterKeyList += DatabaseUtils.sqlEscapeString("" + iter.next());
+ if (iter.hasNext()) {
+ inMasterKeyList += ", ";
+ }
+ }
+ inMasterKeyList += ")";
+ }
+
+ String selection = KeyRings.HAS_ANY_SECRET + " != 0";
+
+ String orderBy = KeyRings.USER_ID + " ASC";
+ if (inMasterKeyList != null) {
+ // sort by selected master keys
+ orderBy = inMasterKeyList + " DESC, " + orderBy;
+ }
+ // 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, selection, null, orderBy);
+ }
+
+ @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);
+
+ // The list should now be shown.
+ if (isResumed()) {
+ setListShown(true);
+ } else {
+ setListShownNoAnimation(true);
+ }
+
+ // preselect given master keys
+ preselectMasterKeyIds(mSelectedMasterKeyIds);
+ }
+
+ @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);
+ }
+
+ private class SecretKeyCursorAdapter extends SelectKeyCursorAdapter {
+
+ public SecretKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView) {
+ super(context, c, flags, listView);
+ }
+
+ @Override
+ protected void initIndex(Cursor cursor) {
+ super.initIndex(cursor);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ super.bindView(view, context, cursor);
+ ViewHolderItem h = (ViewHolderItem) view.getTag();
+
+ // We care about the checkbox
+ h.selected.setVisibility(View.VISIBLE);
+ // the getListView works because this is not a static subclass!
+ h.selected.setChecked(getListView().isItemChecked(cursor.getPosition()));
+
+ boolean enabled = false;
+ if ((Boolean) h.statusIcon.getTag()) {
+ h.statusIcon.setVisibility(View.GONE);
+ enabled = true;
+ }
+
+ h.setEnabled(enabled);
+ }
+
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java
index a6db02708..7beac8973 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java
@@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-public class AppSettingsFragment extends Fragment {
+public class AppSettingsHeaderFragment extends Fragment {
// model
private AppSettings mAppSettings;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java
deleted file mode 100644
index 11b0deb33..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java
+++ /dev/null
@@ -1,36 +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.remote.ui;
-
-import android.os.Bundle;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.DrawerActivity;
-
-public class AppsListActivity extends DrawerActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.api_apps_list_activity);
-
- activateDrawerNavigation(savedInstanceState);
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java
index 976ce20d6..2deb33a67 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2013-2015 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
@@ -48,82 +48,83 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.util.Log;
public class AppsListFragment extends ListFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+ LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener {
- // This is the Adapter being used to display the list's data.
- RegisteredAppsAdapter mAdapter;
+ AppsAdapter mAdapter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- getListView().setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
- String selectedPackageName = mAdapter.getItemPackageName(position);
- boolean installed = mAdapter.getItemIsInstalled(position);
- boolean registered = mAdapter.getItemIsRegistered(position);
-
- if (installed) {
- if (registered) {
- // edit app settings
- Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
- intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName));
- startActivity(intent);
- } else {
- Intent i;
- PackageManager manager = getActivity().getPackageManager();
- try {
- i = manager.getLaunchIntentForPackage(selectedPackageName);
- if (i == null)
- throw new PackageManager.NameNotFoundException();
- // start like the Android launcher would do
- i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- i.addCategory(Intent.CATEGORY_LAUNCHER);
- startActivity(i);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(Constants.TAG, "startApp", e);
- }
- }
- } else {
- try {
- startActivity(new Intent(Intent.ACTION_VIEW,
- Uri.parse("market://details?id=" + selectedPackageName)));
- } catch (ActivityNotFoundException anfe) {
- startActivity(new Intent(Intent.ACTION_VIEW,
- Uri.parse("http://play.google.com/store/apps/details?id=" + selectedPackageName)));
- }
- }
- }
- });
+ 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.api_no_apps));
+ // NOTE: No setEmptyText(), we always have the default entries
// 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 RegisteredAppsAdapter(getActivity(), null, 0);
+ mAdapter = new AppsAdapter(getActivity(), null, 0);
setListAdapter(mAdapter);
- // Loader is started in onResume!
+ // NOTE: Loader is started in onResume!
}
@Override
public void onResume() {
super.onResume();
- // after coming back from Google Play -> reload
+
+ // Start out with a progress indicator.
+ setListShown(false);
+
+ // After coming back from Google Play -> reload
getLoaderManager().restartLoader(0, null, this);
}
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ String selectedPackageName = mAdapter.getItemPackageName(position);
+ boolean installed = mAdapter.getItemIsInstalled(position);
+ boolean registered = mAdapter.getItemIsRegistered(position);
+
+ if (installed) {
+ if (registered) {
+ // Edit app settings
+ Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
+ intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName));
+ startActivity(intent);
+ } else {
+ Intent i;
+ PackageManager manager = getActivity().getPackageManager();
+ try {
+ i = manager.getLaunchIntentForPackage(selectedPackageName);
+ if (i == null) {
+ throw new PackageManager.NameNotFoundException();
+ }
+ // Start like the Android launcher would do
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ i.addCategory(Intent.CATEGORY_LAUNCHER);
+ startActivity(i);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(Constants.TAG, "startApp", e);
+ }
+ }
+ } else {
+ try {
+ startActivity(new Intent(Intent.ACTION_VIEW,
+ Uri.parse("market://details?id=" + selectedPackageName)));
+ } catch (ActivityNotFoundException anfe) {
+ startActivity(new Intent(Intent.ACTION_VIEW,
+ Uri.parse("https://play.google.com/store/apps/details?id=" + selectedPackageName)));
+ }
+ }
+ }
+
private static final String TEMP_COLUMN_NAME = "NAME";
private static final String TEMP_COLUMN_INSTALLED = "INSTALLED";
private static final String TEMP_COLUMN_REGISTERED = "REGISTERED";
private static final String TEMP_COLUMN_ICON_RES_ID = "ICON_RES_ID";
- // These are the Contacts rows that we will retrieve.
static final String[] PROJECTION = new String[]{
ApiApps._ID, // 0
ApiApps.PACKAGE_NAME, // 1
@@ -149,106 +150,17 @@ public class AppsListFragment extends ListFragment 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, PROJECTION, null, null,
+ return new AppsLoader(getActivity(), baseUri, PROJECTION, null, null,
ApiApps.PACKAGE_NAME + " COLLATE LOCALIZED ASC");
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- MatrixCursor availableAppsCursor = new MatrixCursor(new String[]{
- ApiApps._ID,
- ApiApps.PACKAGE_NAME,
- TEMP_COLUMN_NAME,
- TEMP_COLUMN_INSTALLED,
- TEMP_COLUMN_REGISTERED,
- TEMP_COLUMN_ICON_RES_ID
- });
- // NOTE: SORT ascending by package name, this is REQUIRED for CursorJoiner!
- // Drawables taken from projects res/drawables-xxhdpi/ic_launcher.png
- availableAppsCursor.addRow(new Object[]{1, "com.fsck.k9", "K-9 Mail", 0, 0, R.drawable.apps_k9});
- availableAppsCursor.addRow(new Object[]{1, "com.zeapo.pwdstore", "Password Store", 0, 0, R.drawable.apps_password_store});
- availableAppsCursor.addRow(new Object[]{1, "eu.siacs.conversations", "Conversations (Instant Messaging)", 0, 0, R.drawable.apps_conversations});
-
- MatrixCursor mergedCursor = new MatrixCursor(new String[]{
- ApiApps._ID,
- ApiApps.PACKAGE_NAME,
- TEMP_COLUMN_NAME,
- TEMP_COLUMN_INSTALLED,
- TEMP_COLUMN_REGISTERED,
- TEMP_COLUMN_ICON_RES_ID
- });
-
- CursorJoiner joiner = new CursorJoiner(
- availableAppsCursor,
- new String[]{ApiApps.PACKAGE_NAME},
- data,
- new String[]{ApiApps.PACKAGE_NAME});
- for (CursorJoiner.Result joinerResult : joiner) {
- switch (joinerResult) {
- case LEFT: {
- // handle case where a row in availableAppsCursor is unique
- String packageName = availableAppsCursor.getString(INDEX_PACKAGE_NAME);
-
- mergedCursor.addRow(new Object[]{
- 1, // no need for unique _ID
- packageName,
- availableAppsCursor.getString(INDEX_NAME),
- isInstalled(packageName),
- 0,
- availableAppsCursor.getInt(INDEX_ICON_RES_ID)
- });
- break;
- }
- case RIGHT: {
- // handle case where a row in data is unique
- String packageName = data.getString(INDEX_PACKAGE_NAME);
-
- mergedCursor.addRow(new Object[]{
- 1, // no need for unique _ID
- packageName,
- null,
- isInstalled(packageName),
- 1, // registered!
- R.drawable.ic_launcher // icon is retrieved later
- });
- break;
- }
- case BOTH: {
- // handle case where a row with the same key is in both cursors
- String packageName = data.getString(INDEX_PACKAGE_NAME);
-
- String name;
- if (isInstalled(packageName) == 1) {
- name = data.getString(INDEX_NAME);
- } else {
- // if not installed take name from available apps list
- name = availableAppsCursor.getString(INDEX_NAME);
- }
-
- mergedCursor.addRow(new Object[]{
- 1, // no need for unique _ID
- packageName,
- name,
- isInstalled(packageName),
- 1, // registered!
- R.drawable.ic_launcher // icon is retrieved later
- });
- break;
- }
- }
- }
-
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
- mAdapter.swapCursor(mergedCursor);
- }
+ mAdapter.swapCursor(data);
- private int isInstalled(String packageName) {
- try {
- getActivity().getPackageManager().getApplicationInfo(packageName, 0);
- return 1;
- } catch (final PackageManager.NameNotFoundException e) {
- return 0;
- }
+ // The list should now be shown.
+ setListShown(true);
}
public void onLoaderReset(Loader<Cursor> loader) {
@@ -258,12 +170,127 @@ public class AppsListFragment extends ListFragment implements
mAdapter.swapCursor(null);
}
- private class RegisteredAppsAdapter extends CursorAdapter {
+ /**
+ * Besides the queried cursor with all registered apps, this loader also returns non-installed
+ * proposed apps using a MatrixCursor.
+ */
+ private static class AppsLoader extends CursorLoader {
+
+ public AppsLoader(Context context) {
+ super(context);
+ }
+
+ public AppsLoader(Context context, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ super(context, uri, projection, selection, selectionArgs, sortOrder);
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+ // Load registered apps from content provider
+ Cursor data = super.loadInBackground();
+
+ MatrixCursor availableAppsCursor = new MatrixCursor(new String[]{
+ ApiApps._ID,
+ ApiApps.PACKAGE_NAME,
+ TEMP_COLUMN_NAME,
+ TEMP_COLUMN_INSTALLED,
+ TEMP_COLUMN_REGISTERED,
+ TEMP_COLUMN_ICON_RES_ID
+ });
+ // NOTE: SORT ascending by package name, this is REQUIRED for CursorJoiner!
+ // Drawables taken from projects res/drawables-xxhdpi/ic_launcher.png
+ availableAppsCursor.addRow(new Object[]{1, "com.fsck.k9", "K-9 Mail", 0, 0, R.drawable.apps_k9});
+ availableAppsCursor.addRow(new Object[]{1, "com.zeapo.pwdstore", "Password Store", 0, 0, R.drawable.apps_password_store});
+ availableAppsCursor.addRow(new Object[]{1, "eu.siacs.conversations", "Conversations (Instant Messaging)", 0, 0, R.drawable.apps_conversations});
+
+ MatrixCursor mergedCursor = new MatrixCursor(new String[]{
+ ApiApps._ID,
+ ApiApps.PACKAGE_NAME,
+ TEMP_COLUMN_NAME,
+ TEMP_COLUMN_INSTALLED,
+ TEMP_COLUMN_REGISTERED,
+ TEMP_COLUMN_ICON_RES_ID
+ });
+
+ CursorJoiner joiner = new CursorJoiner(
+ availableAppsCursor,
+ new String[]{ApiApps.PACKAGE_NAME},
+ data,
+ new String[]{ApiApps.PACKAGE_NAME});
+ for (CursorJoiner.Result joinerResult : joiner) {
+ switch (joinerResult) {
+ case LEFT: {
+ // handle case where a row in availableAppsCursor is unique
+ String packageName = availableAppsCursor.getString(INDEX_PACKAGE_NAME);
+
+ mergedCursor.addRow(new Object[]{
+ 1, // no need for unique _ID
+ packageName,
+ availableAppsCursor.getString(INDEX_NAME),
+ isInstalled(packageName),
+ 0,
+ availableAppsCursor.getInt(INDEX_ICON_RES_ID)
+ });
+ break;
+ }
+ case RIGHT: {
+ // handle case where a row in data is unique
+ String packageName = data.getString(INDEX_PACKAGE_NAME);
+
+ mergedCursor.addRow(new Object[]{
+ 1, // no need for unique _ID
+ packageName,
+ null,
+ isInstalled(packageName),
+ 1, // registered!
+ R.drawable.ic_launcher // icon is retrieved later
+ });
+ break;
+ }
+ case BOTH: {
+ // handle case where a row with the same key is in both cursors
+ String packageName = data.getString(INDEX_PACKAGE_NAME);
+
+ String name;
+ if (isInstalled(packageName) == 1) {
+ name = data.getString(INDEX_NAME);
+ } else {
+ // if not installed take name from available apps list
+ name = availableAppsCursor.getString(INDEX_NAME);
+ }
+
+ mergedCursor.addRow(new Object[]{
+ 1, // no need for unique _ID
+ packageName,
+ name,
+ isInstalled(packageName),
+ 1, // registered!
+ R.drawable.ic_launcher // icon is retrieved later
+ });
+ break;
+ }
+ }
+ }
+
+ return mergedCursor;
+ }
+
+ private int isInstalled(String packageName) {
+ try {
+ getContext().getPackageManager().getApplicationInfo(packageName, 0);
+ return 1;
+ } catch (final PackageManager.NameNotFoundException e) {
+ return 0;
+ }
+ }
+ }
+
+ private class AppsAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private PackageManager mPM;
- public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
+ public AppsAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
@@ -273,44 +300,23 @@ public class AppsListFragment extends ListFragment implements
/**
* Similar to CursorAdapter.getItemId().
* Required to build Uris for api apps, which are not based on row ids
- *
- * @param position
- * @return
*/
public String getItemPackageName(int position) {
- if (mDataValid && mCursor != null) {
- if (mCursor.moveToPosition(position)) {
- return mCursor.getString(INDEX_PACKAGE_NAME);
- } else {
- return null;
- }
+ if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
+ return mCursor.getString(INDEX_PACKAGE_NAME);
} else {
return null;
}
}
public boolean getItemIsInstalled(int position) {
- if (mDataValid && mCursor != null) {
- if (mCursor.moveToPosition(position)) {
- return (mCursor.getInt(INDEX_INSTALLED) == 1);
- } else {
- return false;
- }
- } else {
- return false;
- }
+ return mDataValid && mCursor != null
+ && mCursor.moveToPosition(position) && (mCursor.getInt(INDEX_INSTALLED) == 1);
}
public boolean getItemIsRegistered(int position) {
- if (mDataValid && mCursor != null) {
- if (mCursor.moveToPosition(position)) {
- return (mCursor.getInt(INDEX_REGISTERED) == 1);
- } else {
- return false;
- }
- } else {
- return false;
- }
+ return mDataValid && mCursor != null
+ && mCursor.moveToPosition(position) && (mCursor.getInt(INDEX_REGISTERED) == 1);
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
index d7b723eb0..2c5c78161 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
@@ -22,7 +22,6 @@ import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@@ -39,14 +38,14 @@ 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.BaseActivity;
import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
-import org.sufficientlysecure.keychain.ui.util.ActionBarHelper;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
-public class RemoteServiceActivity extends ActionBarActivity {
+public class RemoteServiceActivity extends BaseActivity {
public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER";
public static final String ACTION_CREATE_ACCOUNT = Constants.INTENT_PREFIX
@@ -76,7 +75,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
public static final String EXTRA_ERROR_MESSAGE = "error_message";
// register view
- private AppSettingsFragment mAppSettingsFragment;
+ private AppSettingsHeaderFragment mAppSettingsHeaderFragment;
// create acc view
private AccountSettingsFragment mAccSettingsFragment;
// select pub keys view
@@ -96,235 +95,250 @@ public class RemoteServiceActivity extends ActionBarActivity {
handleActions(getIntent(), savedInstanceState);
}
+ @Override
+ protected void initLayout() {
+ // done in handleActions()
+ }
+
protected void handleActions(Intent intent, Bundle savedInstanceState) {
String action = intent.getAction();
final Bundle extras = intent.getExtras();
- 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);
+ switch (action) {
+ case ACTION_REGISTER: {
+ final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
+ final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
+ Log.d(Constants.TAG, "ACTION_REGISTER packageName: " + packageName);
- setContentView(R.layout.api_remote_register_app);
+ setContentView(R.layout.api_remote_register_app);
+ initToolbar();
- mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_app_settings_fragment);
+ mAppSettingsHeaderFragment = (AppSettingsHeaderFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
- AppSettings settings = new AppSettings(packageName, packageSignature);
- mAppSettingsFragment.setAppSettings(settings);
+ AppSettings settings = new AppSettings(packageName, packageSignature);
+ mAppSettingsHeaderFragment.setAppSettings(settings);
- // Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setTwoButtonView(getSupportActionBar(),
- R.string.api_register_allow, R.drawable.ic_action_done,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Allow
+ // Inflate a "Done"/"Cancel" custom action bar view
+ setFullScreenDialogTwoButtons(
+ R.string.api_register_allow, R.drawable.ic_check_white_24dp,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Allow
- mProviderHelper.insertApiApp(mAppSettingsFragment.getAppSettings());
+ mProviderHelper.insertApiApp(mAppSettingsHeaderFragment.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();
+ // 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_close_white_24dp,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Disallow
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
+ }
}
- }
- );
- } else if (ACTION_CREATE_ACCOUNT.equals(action)) {
- final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
- final String accName = extras.getString(EXTRA_ACC_NAME);
+ );
+ break;
+ }
+ case ACTION_CREATE_ACCOUNT: {
+ final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
+ final String accName = extras.getString(EXTRA_ACC_NAME);
- setContentView(R.layout.api_remote_create_account);
+ setContentView(R.layout.api_remote_create_account);
+ initToolbar();
- mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_account_settings_fragment);
+ mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_account_settings_fragment);
- TextView text = (TextView) findViewById(R.id.api_remote_create_account_text);
+ TextView text = (TextView) findViewById(R.id.api_remote_create_account_text);
- // update existing?
- Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(packageName, accName);
- AccountSettings settings = mProviderHelper.getApiAccountSettings(uri);
- if (settings == null) {
- // create new account
- settings = new AccountSettings(accName);
- mUpdateExistingAccount = false;
+ // update existing?
+ Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(packageName, accName);
+ AccountSettings settings = mProviderHelper.getApiAccountSettings(uri);
+ if (settings == null) {
+ // create new account
+ settings = new AccountSettings(accName);
+ mUpdateExistingAccount = false;
- text.setText(R.string.api_create_account_text);
- } else {
- // update existing account
- mUpdateExistingAccount = true;
+ text.setText(R.string.api_create_account_text);
+ } else {
+ // update existing account
+ mUpdateExistingAccount = true;
- text.setText(R.string.api_update_account_text);
- }
- mAccSettingsFragment.setAccSettings(settings);
-
- // 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 it has explicitly requested (None is only allowed for new accounts)
- if (mUpdateExistingAccount && mAccSettingsFragment.getAccSettings().getKeyId() == Constants.key.none) {
- Notify.showNotify(RemoteServiceActivity.this, getString(R.string.api_register_error_select_key), Notify.Style.ERROR);
- } else {
- if (mUpdateExistingAccount) {
- Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(packageName);
- Uri accountUri = baseUri.buildUpon().appendEncodedPath(accName).build();
- mProviderHelper.updateApiAccount(
- accountUri,
- mAccSettingsFragment.getAccSettings());
+ text.setText(R.string.api_update_account_text);
+ }
+ mAccSettingsFragment.setAccSettings(settings);
+
+ // Inflate a "Done"/"Cancel" custom action bar view
+ setFullScreenDialogDoneClose(R.string.api_settings_save,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Save
+
+ // user needs to select a key if it has explicitly requested (None is only allowed for new accounts)
+ if (mUpdateExistingAccount && mAccSettingsFragment.getAccSettings().getKeyId() == Constants.key.none) {
+ Notify.showNotify(RemoteServiceActivity.this, getString(R.string.api_register_error_select_key), Notify.Style.ERROR);
} else {
- mProviderHelper.insertApiAccount(
- KeychainContract.ApiAccounts.buildBaseUri(packageName),
- mAccSettingsFragment.getAccSettings());
+ if (mUpdateExistingAccount) {
+ Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(packageName);
+ Uri accountUri = baseUri.buildUpon().appendEncodedPath(accName).build();
+ mProviderHelper.updateApiAccount(
+ accountUri,
+ mAccSettingsFragment.getAccSettings());
+ } else {
+ mProviderHelper.insertApiAccount(
+ KeychainContract.ApiAccounts.buildBaseUri(packageName),
+ mAccSettingsFragment.getAccSettings());
+ }
+
+ // give data through for new service call
+ Intent resultData = extras.getParcelable(EXTRA_DATA);
+ RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
+ RemoteServiceActivity.this.finish();
}
-
- // give data through for new service call
- Intent resultData = extras.getParcelable(EXTRA_DATA);
- RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
+ }
+ },
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Cancel
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
RemoteServiceActivity.this.finish();
}
- }
- }, R.string.api_settings_cancel, R.drawable.ic_action_cancel,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Cancel
- RemoteServiceActivity.this.setResult(RESULT_CANCELED);
- RemoteServiceActivity.this.finish();
- }
+ });
+
+ break;
+ }
+ case ACTION_SELECT_PUB_KEYS: {
+ long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
+ boolean noUserIdsCheck = intent.getBooleanExtra(EXTRA_NO_USER_IDS_CHECK, true);
+ ArrayList<String> missingUserIds = intent
+ .getStringArrayListExtra(EXTRA_MISSING_USER_IDS);
+ ArrayList<String> dublicateUserIds = intent
+ .getStringArrayListExtra(EXTRA_DUPLICATE_USER_IDS);
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+ final SpannableString textIntro = new SpannableString(
+ noUserIdsCheck ? getString(R.string.api_select_pub_keys_text_no_user_ids)
+ : getString(R.string.api_select_pub_keys_text)
+ );
+ textIntro.setSpan(new StyleSpan(Typeface.BOLD), 0, textIntro.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append(textIntro);
+
+ if (missingUserIds != null && missingUserIds.size() > 0) {
+ ssb.append("\n\n");
+ ssb.append(getString(R.string.api_select_pub_keys_missing_text));
+ ssb.append("\n");
+ for (String userId : missingUserIds) {
+ SpannableString ss = new SpannableString(userId + "\n");
+ ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append(ss);
}
- );
-
- } else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
- long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
- boolean noUserIdsCheck = intent.getBooleanExtra(EXTRA_NO_USER_IDS_CHECK, true);
- ArrayList<String> missingUserIds = intent
- .getStringArrayListExtra(EXTRA_MISSING_USER_IDS);
- ArrayList<String> dublicateUserIds = intent
- .getStringArrayListExtra(EXTRA_DUPLICATE_USER_IDS);
-
- SpannableStringBuilder ssb = new SpannableStringBuilder();
- final SpannableString textIntro = new SpannableString(
- noUserIdsCheck ? getString(R.string.api_select_pub_keys_text_no_user_ids)
- : getString(R.string.api_select_pub_keys_text)
- );
- textIntro.setSpan(new StyleSpan(Typeface.BOLD), 0, textIntro.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- ssb.append(textIntro);
-
- if (missingUserIds != null && missingUserIds.size() > 0) {
- ssb.append("\n\n");
- ssb.append(getString(R.string.api_select_pub_keys_missing_text));
- ssb.append("\n");
- for (String userId : missingUserIds) {
- SpannableString ss = new SpannableString(userId + "\n");
- ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- ssb.append(ss);
}
- }
- if (dublicateUserIds != null && dublicateUserIds.size() > 0) {
- ssb.append("\n\n");
- ssb.append(getString(R.string.api_select_pub_keys_dublicates_text));
- ssb.append("\n");
- for (String userId : dublicateUserIds) {
- SpannableString ss = new SpannableString(userId + "\n");
- ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- ssb.append(ss);
+ if (dublicateUserIds != null && dublicateUserIds.size() > 0) {
+ ssb.append("\n\n");
+ ssb.append(getString(R.string.api_select_pub_keys_dublicates_text));
+ ssb.append("\n");
+ for (String userId : dublicateUserIds) {
+ SpannableString ss = new SpannableString(userId + "\n");
+ ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append(ss);
+ }
}
- }
- // Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setTwoButtonView(getSupportActionBar(),
- R.string.btn_okay, R.drawable.ic_action_done,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // add key ids to params Bundle for new request
- Intent resultData = extras.getParcelable(EXTRA_DATA);
- resultData.putExtra(OpenPgpApi.EXTRA_KEY_IDS,
- mSelectFragment.getSelectedMasterKeyIds());
-
- RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
- RemoteServiceActivity.this.finish();
- }
- }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // cancel
- RemoteServiceActivity.this.setResult(RESULT_CANCELED);
- RemoteServiceActivity.this.finish();
- }
- }
- );
+ setContentView(R.layout.api_remote_select_pub_keys);
+ initToolbar();
- setContentView(R.layout.api_remote_select_pub_keys);
+ // Inflate a "Done"/"Cancel" custom action bar view
+ setFullScreenDialogDoneClose(R.string.btn_okay,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // add key ids to params Bundle for new request
+ Intent resultData = extras.getParcelable(EXTRA_DATA);
+ resultData.putExtra(OpenPgpApi.EXTRA_KEY_IDS,
+ mSelectFragment.getSelectedMasterKeyIds());
- // set text on view
- TextView textView = (TextView) findViewById(R.id.api_select_pub_keys_text);
- textView.setText(ssb, TextView.BufferType.SPANNABLE);
+ RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
+ RemoteServiceActivity.this.finish();
+ }
+ },
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // cancel
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
+ }
+ });
+
+ // set text on view
+ TextView textView = (TextView) findViewById(R.id.api_select_pub_keys_text);
+ textView.setText(ssb, TextView.BufferType.SPANNABLE);
+
+ /* Load select pub keys fragment */
+ // Check that the activity is using the layout version with
+ // the fragment_container FrameLayout
+ if (findViewById(R.id.api_select_pub_keys_fragment_container) != null) {
+
+ // 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;
+ }
- /* Load select pub keys fragment */
- // Check that the activity is using the layout version with
- // the fragment_container FrameLayout
- if (findViewById(R.id.api_select_pub_keys_fragment_container) != null) {
+ // Create an instance of the fragment
+ mSelectFragment = SelectPublicKeyFragment.newInstance(selectedMasterKeyIds);
- // 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;
+ // Add the fragment to the 'fragment_container' FrameLayout
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.api_select_pub_keys_fragment_container, mSelectFragment).commit();
}
-
- // Create an instance of the fragment
- mSelectFragment = SelectPublicKeyFragment.newInstance(selectedMasterKeyIds);
-
- // Add the fragment to the 'fragment_container' FrameLayout
- getSupportFragmentManager().beginTransaction()
- .add(R.id.api_select_pub_keys_fragment_container, mSelectFragment).commit();
+ break;
}
- } else if (ACTION_ERROR_MESSAGE.equals(action)) {
- String errorMessage = intent.getStringExtra(EXTRA_ERROR_MESSAGE);
+ case ACTION_ERROR_MESSAGE: {
+ String errorMessage = intent.getStringExtra(EXTRA_ERROR_MESSAGE);
- Spannable redErrorMessage = new SpannableString(errorMessage);
- redErrorMessage.setSpan(new ForegroundColorSpan(Color.RED), 0, errorMessage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ Spannable redErrorMessage = new SpannableString(errorMessage);
+ redErrorMessage.setSpan(new ForegroundColorSpan(Color.RED), 0, errorMessage.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- // Inflate a "Done" custom action bar view
- ActionBarHelper.setOneButtonView(getSupportActionBar(),
- R.string.btn_okay, R.drawable.ic_action_done,
- new View.OnClickListener() {
+ setContentView(R.layout.api_remote_error_message);
+ initToolbar();
- @Override
- public void onClick(View v) {
- RemoteServiceActivity.this.setResult(RESULT_CANCELED);
- RemoteServiceActivity.this.finish();
- }
- }
- );
+ // Inflate a "Done" custom action bar view
+ setFullScreenDialogClose(
+ new View.OnClickListener() {
- setContentView(R.layout.api_remote_error_message);
+ @Override
+ public void onClick(View v) {
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
+ }
+ }
+ );
- // set text on view
- TextView textView = (TextView) findViewById(R.id.api_app_error_message_text);
- textView.setText(redErrorMessage);
- } else {
- Log.e(Constants.TAG, "Action does not exist!");
- setResult(RESULT_CANCELED);
- finish();
+ // set text on view
+ TextView textView = (TextView) findViewById(R.id.api_app_error_message_text);
+ textView.setText(redErrorMessage);
+ break;
+ }
+ default:
+ Log.e(Constants.TAG, "Action does not exist!");
+ setResult(RESULT_CANCELED);
+ finish();
+ break;
}
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
index dd9c0d769..f0dbf0820 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
@@ -34,7 +34,7 @@ public class CertifyActionsParcel implements Parcelable {
final public long mMasterKeyId;
public CertifyLevel mLevel;
- public ArrayList<CertifyAction> mCertifyActions = new ArrayList<CertifyAction>();
+ public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
public CertifyActionsParcel(long masterKeyId) {
mMasterKeyId = masterKeyId;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
index 447f413ef..7688b9252 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
@@ -53,7 +53,7 @@ public class ContactSyncAdapterService extends Service {
// new Handler.Callback() {
// @Override
// public boolean handleMessage(Message msg) {
-// Bundle data = msg.getData();
+// Bundle data = msg.getInputData();
// switch (msg.arg1) {
// case KeychainIntentServiceHandler.MESSAGE_OKAY:
// Log.d(Constants.TAG, "Syncing... Done.");
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index 3626076b7..0ddf0c76d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -26,10 +26,25 @@ import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import com.textuality.keybase.lib.Proof;
+import com.textuality.keybase.lib.prover.Prover;
+
+import org.json.JSONObject;
+import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
+import org.sufficientlysecure.keychain.keyimport.Keyserver;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.CertifyOperation;
import org.sufficientlysecure.keychain.operations.DeleteOperation;
import org.sufficientlysecure.keychain.operations.EditKeyOperation;
+import org.sufficientlysecure.keychain.operations.ImportExportOperation;
+import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
+import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
+import org.sufficientlysecure.keychain.operations.results.CertifyResult;
+import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
@@ -41,35 +56,40 @@ import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
+import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.operations.ImportExportOperation;
-import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
-import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache;
+import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import java.io.ByteArrayInputStream;
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.Date;
-import java.util.Iterator;
+import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import de.measite.minidns.Client;
+import de.measite.minidns.DNSMessage;
+import de.measite.minidns.Question;
+import de.measite.minidns.Record;
+import de.measite.minidns.record.Data;
+import de.measite.minidns.record.TXT;
+
/**
* This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
* data from the activities or other apps, queues these intents, executes them, and stops itself
@@ -86,10 +106,14 @@ public class KeychainIntentService extends IntentService implements Progressable
public static final String ACTION_DECRYPT_VERIFY = Constants.INTENT_PREFIX + "DECRYPT_VERIFY";
+ public static final String ACTION_VERIFY_KEYBASE_PROOF = Constants.INTENT_PREFIX + "VERIFY_KEYBASE_PROOF";
+
public static final String ACTION_DECRYPT_METADATA = Constants.INTENT_PREFIX + "DECRYPT_METADATA";
public static final String ACTION_EDIT_KEYRING = Constants.INTENT_PREFIX + "EDIT_KEYRING";
+ public static final String ACTION_PROMOTE_KEYRING = Constants.INTENT_PREFIX + "PROMOTE_KEYRING";
+
public static final String ACTION_IMPORT_KEYRING = Constants.INTENT_PREFIX + "IMPORT_KEYRING";
public static final String ACTION_EXPORT_KEYRING = Constants.INTENT_PREFIX + "EXPORT_KEYRING";
@@ -108,33 +132,25 @@ public class KeychainIntentService extends IntentService implements Progressable
// encrypt, decrypt, import export
public static final String TARGET = "target";
public static final String SOURCE = "source";
+
// possible targets:
public static final int IO_BYTES = 1;
public static final int IO_URI = 2;
- public static final int IO_URIS = 3;
-
- public static final String SELECTED_URI = "selected_uri";
// encrypt
- public static final String ENCRYPT_SIGNATURE_MASTER_ID = "secret_key_id";
- public static final String ENCRYPT_SIGNATURE_KEY_PASSPHRASE = "secret_key_passphrase";
- public static final String ENCRYPT_SIGNATURE_NFC_TIMESTAMP = "signature_nfc_timestamp";
- public static final String ENCRYPT_SIGNATURE_NFC_HASH = "signature_nfc_hash";
- public static final String ENCRYPT_USE_ASCII_ARMOR = "use_ascii_armor";
- public static final String ENCRYPT_ENCRYPTION_KEYS_IDS = "encryption_keys_ids";
- public static final String ENCRYPT_COMPRESSION_ID = "compression_id";
- public static final String ENCRYPT_MESSAGE_BYTES = "message_bytes";
public static final String ENCRYPT_DECRYPT_INPUT_URI = "input_uri";
- public static final String ENCRYPT_INPUT_URIS = "input_uris";
public static final String ENCRYPT_DECRYPT_OUTPUT_URI = "output_uri";
- public static final String ENCRYPT_OUTPUT_URIS = "output_uris";
- public static final String ENCRYPT_SYMMETRIC_PASSPHRASE = "passphrase";
+ public static final String SIGN_ENCRYPT_PARCEL = "sign_encrypt_parcel";
// decrypt/verify
public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes";
public static final String DECRYPT_PASSPHRASE = "passphrase";
public static final String DECRYPT_NFC_DECRYPTED_SESSION_KEY = "nfc_decrypted_session_key";
+ // keybase proof
+ public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint";
+ public static final String KEYBASE_PROOF = "keybase_proof";
+
// save keyring
public static final String EDIT_KEYRING_PARCEL = "save_parcel";
public static final String EDIT_KEYRING_PASSPHRASE = "passphrase";
@@ -160,6 +176,10 @@ public class KeychainIntentService extends IntentService implements Progressable
// certify key
public static final String CERTIFY_PARCEL = "certify_parcel";
+ // promote key
+ public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id";
+ public static final String PROMOTE_TYPE = "promote_type";
+
// consolidate
public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
@@ -168,9 +188,6 @@ public class KeychainIntentService extends IntentService implements Progressable
* possible data keys as result send over messenger
*/
- // encrypt
- public static final String RESULT_BYTES = "encrypted_data";
-
// decrypt/verify
public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
@@ -223,303 +240,357 @@ public class KeychainIntentService extends IntentService implements Progressable
String action = intent.getAction();
// executeServiceMethod action from extra bundle
- if (ACTION_CERTIFY_KEYRING.equals(action)) {
+ switch (action) {
+ case ACTION_CERTIFY_KEYRING: {
- // Input
- CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL);
- String keyServerUri = data.getString(UPLOAD_KEY_SERVER);
-
- // Operation
- CertifyOperation op = new CertifyOperation(this, providerHelper, this, mActionCanceled);
- CertifyResult result = op.certify(parcel, keyServerUri);
+ // Input
+ CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL);
+ String keyServerUri = data.getString(UPLOAD_KEY_SERVER);
- // Result
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
+ // Operation
+ CertifyOperation op = new CertifyOperation(this, providerHelper, this, mActionCanceled);
+ CertifyResult result = op.certify(parcel, keyServerUri);
- } else if (ACTION_CONSOLIDATE.equals(action)) {
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- // Operation
- ConsolidateResult result;
- if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
- result = new ProviderHelper(this).consolidateDatabaseStep2(this);
- } else {
- result = new ProviderHelper(this).consolidateDatabaseStep1(this);
+ break;
}
+ case ACTION_CONSOLIDATE: {
- // Result
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
+ // Operation
+ ConsolidateResult result;
+ if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
+ result = new ProviderHelper(this).consolidateDatabaseStep2(this);
+ } else {
+ result = new ProviderHelper(this).consolidateDatabaseStep1(this);
+ }
- } else if (ACTION_DECRYPT_METADATA.equals(action)) {
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- try {
+ break;
+ }
+ case ACTION_DECRYPT_METADATA: {
+
+ try {
/* Input */
- String passphrase = data.getString(DECRYPT_PASSPHRASE);
- byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
+ String passphrase = data.getString(DECRYPT_PASSPHRASE);
+ byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
- InputData inputData = createDecryptInputData(data);
+ InputData inputData = createDecryptInputData(data);
/* Operation */
- Bundle resultData = new Bundle();
+ Bundle resultData = new Bundle();
+
+ // verifyText and decrypt returning additional resultData values for the
+ // verification of signatures
+ PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
+ this, new ProviderHelper(this), this, inputData, null
+ );
+ builder.setAllowSymmetricDecryption(true)
+ .setPassphrase(passphrase)
+ .setDecryptMetadataOnly(true)
+ .setNfcState(nfcDecryptedSessionKey);
- // verifyText and decrypt returning additional resultData values for the
- // verification of signatures
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(this), this, inputData, null
- );
- builder.setAllowSymmetricDecryption(true)
- .setPassphrase(passphrase)
- .setDecryptMetadataOnly(true)
- .setNfcState(nfcDecryptedSessionKey);
+ DecryptVerifyResult decryptVerifyResult = builder.build().execute();
- DecryptVerifyResult decryptVerifyResult = builder.build().execute();
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, decryptVerifyResult);
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, decryptVerifyResult);
- } catch (Exception e) {
- sendErrorToHandler(e);
+ break;
}
+ case ACTION_VERIFY_KEYBASE_PROOF: {
- } else if (ACTION_DECRYPT_VERIFY.equals(action)) {
+ try {
+ Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF)));
+ setProgress(R.string.keybase_message_fetching_data, 0, 100);
- try {
- /* Input */
- String passphrase = data.getString(DECRYPT_PASSPHRASE);
- byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
+ Prover prover = Prover.findProverFor(proof);
- InputData inputData = createDecryptInputData(data);
- OutputStream outStream = createCryptOutputStream(data);
+ if (prover == null) {
+ sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof.getPrettyName());
+ return;
+ }
- /* Operation */
+ if (!prover.fetchProofData()) {
+ sendProofError(prover.getLog(), getString(R.string.keybase_problem_fetching_evidence));
+ return;
+ }
+ String requiredFingerprint = data.getString(KEYBASE_REQUIRED_FINGERPRINT);
+ if (!prover.checkFingerprint(requiredFingerprint)) {
+ sendProofError(getString(R.string.keybase_key_mismatch));
+ return;
+ }
- Bundle resultData = new Bundle();
+ String domain = prover.dnsTxtCheckRequired();
+ if (domain != null) {
+ DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
+ if (dnsQuery == null) {
+ sendProofError(prover.getLog(), getString(R.string.keybase_dns_query_failure));
+ return;
+ }
+ Record[] records = dnsQuery.getAnswers();
+ List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
+ for (Record r : records) {
+ Data d = r.getPayload();
+ if (d instanceof TXT) {
+ extents.add(((TXT) d).getExtents());
+ }
+ }
+ if (!prover.checkDnsTxt(extents)) {
+ sendProofError(prover.getLog(), null);
+ return;
+ }
+ }
- // verifyText and decrypt returning additional resultData values for the
- // verification of signatures
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(this), this,
- inputData, outStream
- );
- builder.setAllowSymmetricDecryption(true)
- .setPassphrase(passphrase)
- .setNfcState(nfcDecryptedSessionKey);
+ byte[] messageBytes = prover.getPgpMessage().getBytes();
+ if (prover.rawMessageCheckRequired()) {
+ InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(messageBytes));
+ if (!prover.checkRawMessageBytes(messageByteStream)) {
+ sendProofError(prover.getLog(), null);
+ return;
+ }
+ }
- DecryptVerifyResult decryptVerifyResult = builder.build().execute();
+ // kind of awkward, but this whole class wants to pull bytes out of “data”
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES);
+ data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, messageBytes);
- outStream.close();
+ InputData inputData = createDecryptInputData(data);
+ OutputStream outStream = createCryptOutputStream(data);
- resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult);
+ PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
+ this, new ProviderHelper(this), this,
+ inputData, outStream
+ );
+ builder.setSignedLiteralData(true).setRequiredSignerFingerprint(requiredFingerprint);
- /* Output */
+ DecryptVerifyResult decryptVerifyResult = builder.build().execute();
+ outStream.close();
- finalizeDecryptOutputStream(data, resultData, outStream);
+ if (!decryptVerifyResult.success()) {
+ OperationLog log = decryptVerifyResult.getLog();
+ OperationResult.LogEntryParcel lastEntry = null;
+ for (OperationResult.LogEntryParcel entry : log) {
+ lastEntry = entry;
+ }
+ sendProofError(getString(lastEntry.mType.getMsgId()));
+ return;
+ }
- Log.logDebugBundle(resultData, "resultData");
+ if (!prover.validate(outStream.toString())) {
+ sendProofError(getString(R.string.keybase_message_payload_mismatch));
+ return;
+ }
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
+ Bundle resultData = new Bundle();
+ resultData.putString(KeychainIntentServiceHandler.DATA_MESSAGE, "OK");
- } else if (ACTION_DELETE.equals(action)) {
+ // these help the handler construct a useful human-readable message
+ resultData.putString(KeychainIntentServiceHandler.KEYBASE_PROOF_URL, prover.getProofUrl());
+ resultData.putString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_URL, prover.getPresenceUrl());
+ resultData.putString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_LABEL, prover.getPresenceLabel());
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
- // Input
- long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
- boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
+ break;
+ }
+ case ACTION_DECRYPT_VERIFY: {
- // Operation
- DeleteOperation op = new DeleteOperation(this, new ProviderHelper(this), this);
- DeleteResult result = op.execute(masterKeyIds, isSecret);
+ try {
+ /* Input */
+ String passphrase = data.getString(DECRYPT_PASSPHRASE);
+ byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
- // Result
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
+ InputData inputData = createDecryptInputData(data);
+ OutputStream outStream = createCryptOutputStream(data);
- } else if (ACTION_EDIT_KEYRING.equals(action)) {
+ /* Operation */
- // Input
- SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
- String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE);
+ Bundle resultData = new Bundle();
- // Operation
- EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled);
- EditKeyResult result = op.execute(saveParcel, passphrase);
+ // verifyText and decrypt returning additional resultData values for the
+ // verification of signatures
+ PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
+ this, new ProviderHelper(this), this,
+ inputData, outStream
+ );
+ builder.setAllowSymmetricDecryption(true)
+ .setPassphrase(passphrase)
+ .setNfcState(nfcDecryptedSessionKey);
- // Result
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
+ DecryptVerifyResult decryptVerifyResult = builder.build().execute();
- } else if (ACTION_EXPORT_KEYRING.equals(action)) {
+ outStream.close();
- // Input
- boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
- String outputFile = data.getString(EXPORT_FILENAME);
- Uri outputUri = data.getParcelable(EXPORT_URI);
+ resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult);
- boolean exportAll = data.getBoolean(EXPORT_ALL);
- long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
+ /* Output */
- // Operation
- ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this);
- ExportResult result;
- if (outputFile != null) {
- result = importExportOperation.exportToFile(masterKeyIds, exportSecret, outputFile);
- } else {
- result = importExportOperation.exportToUri(masterKeyIds, exportSecret, outputUri);
- }
+ finalizeDecryptOutputStream(data, resultData, outStream);
- // Result
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
+ Log.logDebugBundle(resultData, "resultData");
- } else if (ACTION_IMPORT_KEYRING.equals(action)) {
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
- try {
+ break;
+ }
+ case ACTION_DELETE: {
// Input
- String keyServer = data.getString(IMPORT_KEY_SERVER);
- Iterator<ParcelableKeyRing> entries;
- int numEntries;
- if (data.containsKey(IMPORT_KEY_LIST)) {
- // get entries from intent
- ArrayList<ParcelableKeyRing> list = data.getParcelableArrayList(IMPORT_KEY_LIST);
- entries = list.iterator();
- numEntries = list.size();
- } else {
- // get entries from cached file
- ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl");
- IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
- entries = it;
- numEntries = it.getSize();
- }
+ long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
+ boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
// Operation
- ImportExportOperation importExportOperation = new ImportExportOperation(
- this, providerHelper, this, mActionCanceled);
- ImportKeyResult result = importExportOperation.importKeyRings(entries, numEntries, keyServer);
+ DeleteOperation op = new DeleteOperation(this, new ProviderHelper(this), this);
+ DeleteResult result = op.execute(masterKeyIds, isSecret);
- // Special: consolidate on secret key import (cannot be cancelled!)
- if (result.mSecret > 0) {
- // TODO move this into the import operation
- providerHelper.consolidateDatabaseStep1(this);
- }
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- // Special: make sure new data is synced into contacts
- ContactSyncAdapterService.requestSync();
+ break;
+ }
+ case ACTION_EDIT_KEYRING: {
+
+ // Input
+ SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
+ String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE);
+
+ // Operation
+ EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled);
+ EditKeyResult result = op.execute(saveParcel, passphrase);
// Result
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- } catch (Exception e) {
- sendErrorToHandler(e);
+
+ break;
}
+ case ACTION_PROMOTE_KEYRING: {
- } else if (ACTION_SIGN_ENCRYPT.equals(action)) {
+ // Input
+ long keyRingId = data.getInt(EXPORT_KEY_RING_MASTER_KEY_ID);
- try {
- /* Input */
- int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
- Bundle resultData = new Bundle();
+ // Operation
+ PromoteKeyOperation op = new PromoteKeyOperation(this, providerHelper, this, mActionCanceled);
+ PromoteKeyResult result = op.execute(keyRingId);
- long sigMasterKeyId = data.getLong(ENCRYPT_SIGNATURE_MASTER_ID);
- String sigKeyPassphrase = data.getString(ENCRYPT_SIGNATURE_KEY_PASSPHRASE);
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- byte[] nfcHash = data.getByteArray(ENCRYPT_SIGNATURE_NFC_HASH);
- Date nfcTimestamp = (Date) data.getSerializable(ENCRYPT_SIGNATURE_NFC_TIMESTAMP);
+ break;
+ }
+ case ACTION_EXPORT_KEYRING: {
- String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE);
+ // Input
+ boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
+ String outputFile = data.getString(EXPORT_FILENAME);
+ Uri outputUri = data.getParcelable(EXPORT_URI);
- boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_ARMOR);
- long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS);
- int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID);
- int urisCount = data.containsKey(ENCRYPT_INPUT_URIS) ? data.getParcelableArrayList(ENCRYPT_INPUT_URIS).size() : 1;
- for (int i = 0; i < urisCount; i++) {
- data.putInt(SELECTED_URI, i);
- InputData inputData = createEncryptInputData(data);
- OutputStream outStream = createCryptOutputStream(data);
- String originalFilename = getOriginalFilename(data);
+ boolean exportAll = data.getBoolean(EXPORT_ALL);
+ long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
- /* Operation */
- PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
- this, new ProviderHelper(this), this, inputData, outStream
- );
- builder.setEnableAsciiArmorOutput(useAsciiArmor)
- .setVersionHeader(PgpHelper.getVersionForHeader(this))
- .setCompressionId(compressionId)
- .setSymmetricEncryptionAlgorithm(
- Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
- .setEncryptionMasterKeyIds(encryptionKeyIds)
- .setSymmetricPassphrase(symmetricPassphrase)
- .setOriginalFilename(originalFilename);
+ // Operation
+ ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this);
+ ExportResult result;
+ if (outputFile != null) {
+ result = importExportOperation.exportToFile(masterKeyIds, exportSecret, outputFile);
+ } else {
+ result = importExportOperation.exportToUri(masterKeyIds, exportSecret, outputUri);
+ }
- try {
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- // Find the appropriate subkey to sign with
- CachedPublicKeyRing signingRing =
- new ProviderHelper(this).getCachedPublicKeyRing(sigMasterKeyId);
- long sigSubKeyId = signingRing.getSecretSignId();
-
- // Set signature settings
- builder.setSignatureMasterKeyId(sigMasterKeyId)
- .setSignatureSubKeyId(sigSubKeyId)
- .setSignaturePassphrase(sigKeyPassphrase)
- .setSignatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .setAdditionalEncryptId(sigMasterKeyId);
- if (nfcHash != null && nfcTimestamp != null) {
- builder.setNfcState(nfcHash, nfcTimestamp);
- }
+ break;
+ }
+ case ACTION_IMPORT_KEYRING: {
- } catch (PgpKeyNotFoundException e) {
- // encrypt-only
- // TODO Just silently drop the requested signature? Shouldn't we throw here?
- }
+ // Input
+ String keyServer = data.getString(IMPORT_KEY_SERVER);
+ ArrayList<ParcelableKeyRing> list = data.getParcelableArrayList(IMPORT_KEY_LIST);
+ ParcelableFileCache<ParcelableKeyRing> cache =
+ new ParcelableFileCache<>(this, "key_import.pcl");
- // this assumes that the bytes are cleartext (valid for current implementation!)
- if (source == IO_BYTES) {
- builder.setCleartextInput(true);
- }
+ // Operation
+ ImportExportOperation importExportOperation = new ImportExportOperation(
+ this, providerHelper, this, mActionCanceled);
+ // Either list or cache must be null, no guarantees otherwise.
+ ImportKeyResult result = list != null
+ ? importExportOperation.importKeyRings(list, keyServer)
+ : importExportOperation.importKeyRings(cache, keyServer);
- SignEncryptResult result = builder.build().execute();
- resultData.putParcelable(SignEncryptResult.EXTRA_RESULT, result);
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- outStream.close();
+ break;
- /* Output */
+ }
+ case ACTION_SIGN_ENCRYPT: {
- finalizeEncryptOutputStream(data, resultData, outStream);
+ // Input
+ SignEncryptParcel inputParcel = data.getParcelable(SIGN_ENCRYPT_PARCEL);
- }
+ // Operation
+ SignEncryptOperation op = new SignEncryptOperation(
+ this, new ProviderHelper(this), this, mActionCanceled);
+ SignEncryptResult result = op.execute(inputParcel);
- Log.logDebugBundle(resultData, "resultData");
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
- } catch (Exception e) {
- sendErrorToHandler(e);
+ break;
}
+ case ACTION_UPLOAD_KEYRING: {
+ try {
- } else if (ACTION_UPLOAD_KEYRING.equals(action)) {
-
- try {
+ /* Input */
+ String keyServer = data.getString(UPLOAD_KEY_SERVER);
+ // and dataUri!
- /* Input */
- String keyServer = data.getString(UPLOAD_KEY_SERVER);
- // and dataUri!
+ /* Operation */
+ HkpKeyserver server = new HkpKeyserver(keyServer);
- /* Operation */
- HkpKeyserver server = new HkpKeyserver(keyServer);
+ CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
+ ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this);
- CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
- ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this);
+ try {
+ importExportOperation.uploadKeyRingToServer(server, keyring);
+ } catch (Keyserver.AddKeyException e) {
+ throw new PgpGeneralException("Unable to export key to selected server");
+ }
- try {
- importExportOperation.uploadKeyRingToServer(server, keyring);
- } catch (Keyserver.AddKeyException e) {
- throw new PgpGeneralException("Unable to export key to selected server");
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+ } catch (Exception e) {
+ sendErrorToHandler(e);
}
-
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
- } catch (Exception e) {
- sendErrorToHandler(e);
+ break;
}
}
+ }
+
+ private void sendProofError(List<String> log, String label) {
+ String msg = null;
+ label = (label == null) ? "" : label + ": ";
+ for (String m : log) {
+ Log.e(Constants.TAG, label + m);
+ msg = m;
+ }
+ sendProofError(label + msg);
+ }
+ private void sendProofError(String msg) {
+ Bundle bundle = new Bundle();
+ bundle.putString(KeychainIntentServiceHandler.DATA_ERROR, msg);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, bundle);
}
private void sendErrorToHandler(Exception e) {
@@ -532,7 +603,6 @@ public class KeychainIntentService extends IntentService implements Progressable
} else {
message = e.getMessage();
}
-
Log.d(Constants.TAG, "KeychainIntentService Exception: ", e);
Bundle data = new Bundle();
@@ -609,10 +679,6 @@ public class KeychainIntentService extends IntentService implements Progressable
return createCryptInputData(data, DECRYPT_CIPHERTEXT_BYTES);
}
- private InputData createEncryptInputData(Bundle data) throws IOException, PgpGeneralException {
- return createCryptInputData(data, ENCRYPT_MESSAGE_BYTES);
- }
-
private InputData createCryptInputData(Bundle data, String bytesName) throws PgpGeneralException, IOException {
int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
switch (source) {
@@ -626,33 +692,6 @@ public class KeychainIntentService extends IntentService implements Progressable
// InputStream
return new InputData(getContentResolver().openInputStream(providerUri), FileHelper.getFileSize(this, providerUri, 0));
- case IO_URIS:
- providerUri = data.<Uri>getParcelableArrayList(ENCRYPT_INPUT_URIS).get(data.getInt(SELECTED_URI));
-
- // InputStream
- return new InputData(getContentResolver().openInputStream(providerUri), FileHelper.getFileSize(this, providerUri, 0));
-
- default:
- throw new PgpGeneralException("No target choosen!");
- }
- }
-
- private String getOriginalFilename(Bundle data) throws PgpGeneralException, FileNotFoundException {
- int target = data.getInt(TARGET);
- switch (target) {
- case IO_BYTES:
- return "";
-
- case IO_URI:
- Uri providerUri = data.getParcelable(ENCRYPT_DECRYPT_INPUT_URI);
-
- return FileHelper.getFilename(this, providerUri);
-
- case IO_URIS:
- providerUri = data.<Uri>getParcelableArrayList(ENCRYPT_INPUT_URIS).get(data.getInt(SELECTED_URI));
-
- return FileHelper.getFilename(this, providerUri);
-
default:
throw new PgpGeneralException("No target choosen!");
}
@@ -669,20 +708,11 @@ public class KeychainIntentService extends IntentService implements Progressable
return getContentResolver().openOutputStream(providerUri);
- case IO_URIS:
- providerUri = data.<Uri>getParcelableArrayList(ENCRYPT_OUTPUT_URIS).get(data.getInt(SELECTED_URI));
-
- return getContentResolver().openOutputStream(providerUri);
-
default:
throw new PgpGeneralException("No target choosen!");
}
}
- private void finalizeEncryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream) {
- finalizeCryptOutputStream(data, resultData, outStream, RESULT_BYTES);
- }
-
private void finalizeDecryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream) {
finalizeCryptOutputStream(data, resultData, outStream, RESULT_DECRYPTED_BYTES);
}
@@ -695,7 +725,6 @@ public class KeychainIntentService extends IntentService implements Progressable
resultData.putByteArray(bytesName, output);
break;
case IO_URI:
- case IO_URIS:
// nothing, output was written, just send okay and verification bundle
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
index 180020d0b..635fe86f1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
@@ -27,8 +27,8 @@ import android.support.v4.app.FragmentManager;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.util.Log;
public class KeychainIntentServiceHandler extends Handler {
@@ -45,6 +45,11 @@ public class KeychainIntentServiceHandler extends Handler {
public static final String DATA_MESSAGE = "message";
public static final String DATA_MESSAGE_ID = "message_id";
+ // keybase proof specific
+ public static final String KEYBASE_PROOF_URL = "keybase_proof_url";
+ public static final String KEYBASE_PRESENCE_URL = "keybase_presence_url";
+ public static final String KEYBASE_PRESENCE_LABEL = "keybase_presence_label";
+
Activity mActivity;
ProgressDialogFragment mProgressDialogFragment;
@@ -73,6 +78,10 @@ public class KeychainIntentServiceHandler extends Handler {
}
public void showProgressDialog(FragmentActivity activity) {
+ if (mProgressDialogFragment == null) {
+ return;
+ }
+
// TODO: This is a hack!, see
// http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
final FragmentManager manager = activity.getSupportFragmentManager();
@@ -89,7 +98,8 @@ public class KeychainIntentServiceHandler extends Handler {
Bundle data = message.getData();
if (mProgressDialogFragment == null) {
- Log.e(Constants.TAG, "Progress has not been updated because mProgressDialogFragment was null!");
+ // Log.e(Constants.TAG,
+ // "Progress has not been updated because mProgressDialogFragment was null!");
return;
}
@@ -131,6 +141,7 @@ public class KeychainIntentServiceHandler extends Handler {
case MESSAGE_PREVENT_CANCEL:
mProgressDialogFragment.setPreventCancel(true);
+ break;
default:
Log.e(Constants.TAG, "unknown handler message!");
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 869d2e71b..57881f8ee 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -39,12 +39,11 @@ import android.support.v4.util.LongSparseArray;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
import java.util.Date;
@@ -103,7 +102,7 @@ public class PassphraseCacheService extends Service {
private BroadcastReceiver mIntentReceiver;
- private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<CachedPassphrase>();
+ private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<>();
Context mContext;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 2f2224b24..e2d0c03c9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -59,7 +59,6 @@ public class SaveKeyringParcel implements Parcelable {
public ArrayList<String> mRevokeUserIds;
public ArrayList<Long> mRevokeSubKeys;
- public ArrayList<Long> mStripSubKeys;
public SaveKeyringParcel() {
reset();
@@ -73,14 +72,30 @@ public class SaveKeyringParcel implements Parcelable {
public void reset() {
mNewUnlock = null;
- mAddUserIds = new ArrayList<String>();
- mAddUserAttribute = new ArrayList<WrappedUserAttribute>();
- mAddSubKeys = new ArrayList<SubkeyAdd>();
+ mAddUserIds = new ArrayList<>();
+ mAddUserAttribute = new ArrayList<>();
+ mAddSubKeys = new ArrayList<>();
mChangePrimaryUserId = null;
- mChangeSubKeys = new ArrayList<SubkeyChange>();
- mRevokeUserIds = new ArrayList<String>();
- mRevokeSubKeys = new ArrayList<Long>();
- mStripSubKeys = new ArrayList<Long>();
+ mChangeSubKeys = new ArrayList<>();
+ mRevokeUserIds = new ArrayList<>();
+ mRevokeSubKeys = new ArrayList<>();
+ }
+
+ /** Returns true iff this parcel does not contain any operations which require a passphrase. */
+ public boolean isRestrictedOnly() {
+ if (mNewUnlock != null || !mAddUserIds.isEmpty() || !mAddUserAttribute.isEmpty()
+ || !mAddSubKeys.isEmpty() || mChangePrimaryUserId != null || !mRevokeSubKeys .isEmpty()
+ || !mRevokeSubKeys.isEmpty()) {
+ return false;
+ }
+
+ for (SubkeyChange change : mChangeSubKeys) {
+ if (change.mRecertify || change.mFlags != null || change.mExpiry != null) {
+ return false;
+ }
+ }
+
+ return true;
}
// performance gain for using Parcelable here would probably be negligible,
@@ -113,26 +128,53 @@ public class SaveKeyringParcel implements Parcelable {
}
public static class SubkeyChange implements Serializable {
- public long mKeyId;
+ public final long mKeyId;
public Integer mFlags;
// this is a long unix timestamp, in seconds (NOT MILLISECONDS!)
public Long mExpiry;
+ // if this flag is true, the key will be recertified even if all above
+ // values are no-ops
+ public boolean mRecertify;
+ // if this flag is true, the subkey should be changed to a stripped key
+ public boolean mDummyStrip;
+ // if this is non-null, the subkey will be changed to a divert-to-card
+ // key for the given serial number
+ public byte[] mDummyDivert;
public SubkeyChange(long keyId) {
mKeyId = keyId;
}
+ public SubkeyChange(long keyId, boolean recertify) {
+ mKeyId = keyId;
+ mRecertify = recertify;
+ }
+
public SubkeyChange(long keyId, Integer flags, Long expiry) {
mKeyId = keyId;
mFlags = flags;
mExpiry = expiry;
}
+ public SubkeyChange(long keyId, boolean dummyStrip, byte[] dummyDivert) {
+ this(keyId, null, null);
+
+ // these flags are mutually exclusive!
+ if (dummyStrip && dummyDivert != null) {
+ throw new AssertionError(
+ "cannot set strip and divert flags at the same time - this is a bug!");
+ }
+ mDummyStrip = dummyStrip;
+ mDummyDivert = dummyDivert;
+ }
+
@Override
public String toString() {
String out = "mKeyId: " + mKeyId + ", ";
out += "mFlags: " + mFlags + ", ";
- out += "mExpiry: " + mExpiry;
+ out += "mExpiry: " + mExpiry + ", ";
+ out += "mDummyStrip: " + mDummyStrip + ", ";
+ out += "mDummyDivert: [" + (mDummyDivert == null ? 0 : mDummyDivert.length) + " bytes]";
return out;
}
@@ -174,7 +216,6 @@ public class SaveKeyringParcel implements Parcelable {
mRevokeUserIds = source.createStringArrayList();
mRevokeSubKeys = (ArrayList<Long>) source.readSerializable();
- mStripSubKeys = (ArrayList<Long>) source.readSerializable();
}
@Override
@@ -197,7 +238,6 @@ public class SaveKeyringParcel implements Parcelable {
destination.writeStringList(mRevokeUserIds);
destination.writeSerializable(mRevokeSubKeys);
- destination.writeSerializable(mStripSubKeys);
}
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
@@ -225,8 +265,7 @@ public class SaveKeyringParcel implements Parcelable {
out += "mChangeSubKeys: " + mChangeSubKeys + "\n";
out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n";
out += "mRevokeUserIds: " + mRevokeUserIds + "\n";
- out += "mRevokeSubKeys: " + mRevokeSubKeys + "\n";
- out += "mStripSubKeys: " + mStripSubKeys;
+ out += "mRevokeSubKeys: " + mRevokeSubKeys;
return out;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java
new file mode 100644
index 000000000..e6c2542a2
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BaseActivity.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 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.app.Activity;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.R;
+
+/**
+ * Setups Toolbar
+ */
+public abstract class BaseActivity extends ActionBarActivity {
+ protected Toolbar mToolbar;
+ protected View mStatusBar;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ initLayout();
+ initToolbar();
+ }
+
+ protected abstract void initLayout();
+
+ protected void initToolbar() {
+ mToolbar = (Toolbar) findViewById(R.id.toolbar);
+ if (mToolbar != null) {
+ setSupportActionBar(mToolbar);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ mStatusBar = findViewById(R.id.status_bar);
+ }
+
+ protected void setActionBarIcon(int iconRes) {
+ mToolbar.setNavigationIcon(iconRes);
+ }
+
+ /**
+ * Inflate custom design to look like a full screen dialog, as specified in Material Design Guidelines
+ * see http://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs
+ */
+ protected void setFullScreenDialogDoneClose(int doneText, View.OnClickListener doneOnClickListener,
+ View.OnClickListener cancelOnClickListener) {
+ setActionBarIcon(R.drawable.ic_close_white_24dp);
+
+ // Inflate the custom action bar view
+ final LayoutInflater inflater = (LayoutInflater) getSupportActionBar().getThemedContext()
+ .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
+ final View customActionBarView = inflater.inflate(R.layout.full_screen_dialog, null);
+
+ TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.full_screen_dialog_done_text));
+ firstTextView.setText(doneText);
+ customActionBarView.findViewById(R.id.full_screen_dialog_done).setOnClickListener(
+ doneOnClickListener);
+
+ getSupportActionBar().setDisplayShowCustomEnabled(true);
+ getSupportActionBar().setDisplayShowTitleEnabled(true);
+ getSupportActionBar().setCustomView(customActionBarView, new ActionBar.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT,
+ Gravity.END));
+ mToolbar.setNavigationOnClickListener(cancelOnClickListener);
+ }
+
+ /**
+ * Close button only
+ */
+ protected void setFullScreenDialogClose(View.OnClickListener cancelOnClickListener) {
+ setActionBarIcon(R.drawable.ic_close_white_24dp);
+ getSupportActionBar().setDisplayShowTitleEnabled(true);
+ mToolbar.setNavigationOnClickListener(cancelOnClickListener);
+ }
+
+ /**
+ * Inflate custom design with two buttons using drawables.
+ * This does not conform to the Material Design Guidelines, but we deviate here as this is used
+ * to indicate "Allow access"/"Disallow access" to the API, which must be clearly indicated
+ */
+ protected void setFullScreenDialogTwoButtons(int firstText, int firstDrawableId, View.OnClickListener firstOnClickListener,
+ int secondText, int secondDrawableId, View.OnClickListener secondOnClickListener) {
+
+ // Inflate the custom action bar view
+ final LayoutInflater inflater = (LayoutInflater) getSupportActionBar().getThemedContext()
+ .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
+ final View customActionBarView = inflater.inflate(
+ R.layout.full_screen_dialog_2, null);
+
+ 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(
+ 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(
+ secondOnClickListener);
+
+ // Show the custom action bar view and hide the normal Home icon and title.
+ getSupportActionBar().setDisplayShowTitleEnabled(false);
+ getSupportActionBar().setDisplayShowHomeEnabled(false);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+ getSupportActionBar().setDisplayShowCustomEnabled(true);
+ getSupportActionBar().setCustomView(customActionBarView, new ActionBar.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java
index f49f8e7cf..b7c80c1ed 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov
+ * Copyright (C) 2015 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
@@ -20,55 +19,43 @@ package org.sufficientlysecure.keychain.ui;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
import android.view.View;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.util.ActionBarHelper;
-import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.Log;
-public class ViewKeyAdvancedActivity extends ActionBarActivity {
+public class CertifyFingerprintActivity extends BaseActivity {
- ExportHelper mExportHelper;
- ProviderHelper mProviderHelper;
+ protected Uri mDataUri;
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mExportHelper = new ExportHelper(this);
- mProviderHelper = new ProviderHelper(this);
-
- // Inflate a "Done" custom action bar
- ActionBarHelper.setOneButtonView(getSupportActionBar(),
- R.string.btn_okay, R.drawable.ic_action_done,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // "Done"
- finish();
- }
- }
- );
-
- setContentView(R.layout.view_key_advanced_activity);
-
- Uri dataUri = getIntent().getData();
- if (dataUri == null) {
+ mDataUri = getIntent().getData();
+ if (mDataUri == null) {
Log.e(Constants.TAG, "Data missing. Should be uri of key!");
finish();
return;
}
- Log.i(Constants.TAG, "mDataUri: " + dataUri.toString());
+ setFullScreenDialogClose(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
- startFragment(savedInstanceState, dataUri);
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ startFragment(savedInstanceState, mDataUri);
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.certify_fingerprint_activity);
+ }
private void startFragment(Bundle savedInstanceState, Uri dataUri) {
// However, if we're being restored from a previous state,
@@ -79,12 +66,12 @@ public class ViewKeyAdvancedActivity extends ActionBarActivity {
}
// Create an instance of the fragment
- ViewKeyAdvancedFragment frag = ViewKeyAdvancedFragment.newInstance(dataUri);
+ CertifyFingerprintFragment frag = CertifyFingerprintFragment.newInstance(dataUri);
// Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
getSupportFragmentManager().beginTransaction()
- .replace(R.id.view_key_advanced_fragment, frag)
+ .replace(R.id.certify_fingerprint_fragment, frag)
.commitAllowingStateLoss();
// do it immediately!
getSupportFragmentManager().executePendingTransactions();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java
new file mode 100644
index 000000000..a6b8a0e85
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2015 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.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+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.ViewGroup;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+
+
+public class CertifyFingerprintFragment extends LoaderFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ static final int REQUEST_CERTIFY = 1;
+
+ public static final String ARG_DATA_URI = "uri";
+
+ private TextView mFingerprint;
+
+ private static final int LOADER_ID_UNIFIED = 0;
+
+ private Uri mDataUri;
+
+ private View mActionNo;
+ private View mActionYes;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CertifyFingerprintFragment newInstance(Uri dataUri) {
+ CertifyFingerprintFragment frag = new CertifyFingerprintFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_DATA_URI, dataUri);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.certify_fingerprint_fragment, getContainer());
+
+ mActionNo = view.findViewById(R.id.certify_fingerprint_button_no);
+ mActionYes = view.findViewById(R.id.certify_fingerprint_button_yes);
+
+ mFingerprint = (TextView) view.findViewById(R.id.certify_fingerprint_fingerprint);
+
+ mActionNo.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ getActivity().finish();
+ }
+ });
+ mActionYes.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ certify(mDataUri);
+ }
+ });
+
+ return root;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ getActivity().finish();
+ return;
+ }
+
+ loadData(dataUri);
+ }
+
+ private void loadData(Uri dataUri) {
+ mDataUri = dataUri;
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ // Prepare the loaders. Either re-connect with an existing ones,
+ // or start new ones.
+ getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
+ }
+
+ static final String[] UNIFIED_PROJECTION = new String[]{
+ KeyRings._ID, KeyRings.FINGERPRINT,
+
+ };
+ static final int INDEX_UNIFIED_FINGERPRINT = 1;
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ setContentShown(false);
+ switch (id) {
+ case LOADER_ID_UNIFIED: {
+ Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ /* TODO better error handling? May cause problems when a key is deleted,
+ * because the notification triggers faster than the activity closes.
+ */
+ // Avoid NullPointerExceptions...
+ if (data.getCount() == 0) {
+ return;
+ }
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ switch (loader.getId()) {
+ case LOADER_ID_UNIFIED: {
+ if (data.moveToFirst()) {
+
+ byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT);
+ String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob);
+ mFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint));
+
+ break;
+ }
+ }
+
+ }
+ setContentShown(true);
+ }
+
+ /**
+ * 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.
+ */
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+
+ private void certify(Uri dataUri) {
+ long keyId = 0;
+ try {
+ keyId = new ProviderHelper(getActivity())
+ .getCachedPublicKeyRing(dataUri)
+ .extractOrGetMasterKeyId();
+ } catch (PgpKeyNotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ }
+ Intent certifyIntent = new Intent(getActivity(), CertifyKeyActivity.class);
+ certifyIntent.putExtras(getActivity().getIntent());
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{keyId});
+ startActivityForResult(certifyIntent, REQUEST_CERTIFY);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // always just pass this one through
+ if (requestCode == REQUEST_CERTIFY) {
+ getActivity().setResult(resultCode, data);
+ getActivity().finish();
+ return;
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index a97e73934..1fb88b182 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -18,24 +18,19 @@
package org.sufficientlysecure.keychain.ui;
-import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
-
import org.sufficientlysecure.keychain.R;
/**
* Signs the specified public key with the specified secret master key
*/
-public class CertifyKeyActivity extends ActionBarActivity {
+public class CertifyKeyActivity extends BaseActivity {
public static final String EXTRA_RESULT = "operation_result";
public static final String EXTRA_KEY_IDS = "extra_key_ids";
public static final String EXTRA_CERTIFY_KEY_ID = "certify_key_id";
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
+ protected void initLayout() {
setContentView(R.layout.certify_key_activity);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
index 841485c7c..e1467ec61 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
@@ -31,6 +31,7 @@ import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcel;
+import android.os.Parcelable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -94,6 +95,7 @@ public class CertifyKeyFragment extends LoaderFragment
private static final int INDEX_IS_REVOKED = 4;
private MultiUserIdsAdapter mUserIdsAdapter;
+ private Messenger mPassthroughMessenger;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
@@ -109,6 +111,9 @@ public class CertifyKeyFragment extends LoaderFragment
return;
}
+ mPassthroughMessenger = getActivity().getIntent().getParcelableExtra(
+ KeychainIntentService.EXTRA_MESSENGER);
+
// preselect certify key id if given
long certifyKeyId = getActivity().getIntent().getLongExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none);
if (certifyKeyId != Constants.key.none) {
@@ -234,7 +239,7 @@ public class CertifyKeyFragment extends LoaderFragment
long lastMasterKeyId = 0;
String lastName = "";
- ArrayList<String> uids = new ArrayList<String>();
+ ArrayList<String> uids = new ArrayList<>();
boolean header = true;
@@ -332,7 +337,6 @@ public class CertifyKeyFragment extends LoaderFragment
}
}
-
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
@@ -362,51 +366,63 @@ public class CertifyKeyFragment extends LoaderFragment
return;
}
+ Bundle data = new Bundle();
+ {
+ // fill values for this action
+ CertifyActionsParcel parcel = new CertifyActionsParcel(mSignMasterKeyId);
+ parcel.mCertifyActions.addAll(certifyActions);
+
+ data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel);
+ if (mUploadKeyCheckbox.isChecked()) {
+ String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
+ data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver);
+ }
+ }
+
// Send all information needed to service to sign key in other thread
Intent intent = new Intent(getActivity(), KeychainIntentService.class);
-
intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING);
-
- // fill values for this action
- CertifyActionsParcel parcel = new CertifyActionsParcel(mSignMasterKeyId);
- parcel.mCertifyActions.addAll(certifyActions);
-
- Bundle data = new Bundle();
- data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel);
- if (mUploadKeyCheckbox.isChecked()) {
- String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
- data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver);
- }
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after signing is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
- getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- Bundle data = message.getData();
- CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
-
- Intent intent = new Intent();
- intent.putExtra(CertifyResult.EXTRA_RESULT, result);
- getActivity().setResult(Activity.RESULT_OK, intent);
- getActivity().finish();
+ if (mPassthroughMessenger != null) {
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, mPassthroughMessenger);
+ } else {
+
+ // Message is received after signing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ Bundle data = message.getData();
+ CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
+
+ Intent intent = new Intent();
+ intent.putExtra(CertifyResult.EXTRA_RESULT, result);
+ getActivity().setResult(Activity.RESULT_OK, intent);
+ getActivity().finish();
+ }
}
- }
- };
+ };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+ }
// start service with intent
getActivity().startService(intent);
+
+ if (mPassthroughMessenger != null) {
+ getActivity().setResult(Activity.RESULT_OK);
+ getActivity().finish();
+ }
+
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
index 0d8905c5b..f0ef8b9ef 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
@@ -61,7 +61,7 @@ public class ConsolidateDialogActivity extends FragmentActivity {
/* don't care about the results (for now?)
// get returned data bundle
- Bundle returnData = message.getData();
+ Bundle returnData = message.getInputData();
if (returnData == null) {
return;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
index 534ac5811..62c38d136 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -20,11 +20,10 @@ package org.sufficientlysecure.keychain.ui;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
-import android.support.v7.app.ActionBarActivity;
import org.sufficientlysecure.keychain.R;
-public class CreateKeyActivity extends ActionBarActivity {
+public class CreateKeyActivity extends BaseActivity {
public static final String EXTRA_NAME = "name";
public static final String EXTRA_EMAIL = "email";
@@ -37,8 +36,6 @@ public class CreateKeyActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.create_key_activity);
-
// pass extras into fragment
CreateKeyInputFragment frag =
CreateKeyInputFragment.newInstance(
@@ -48,6 +45,11 @@ public class CreateKeyActivity extends ActionBarActivity {
loadFragment(null, frag, FRAG_ACTION_START);
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.create_key_activity);
+ }
+
public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
index 2804a5ce6..6e0115342 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -35,17 +35,16 @@ import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
-import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
-import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
public class CreateKeyFinalFragment extends Fragment {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java
index 6079062a7..8aa9fa6db 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java
@@ -89,7 +89,7 @@ public class CreateKeyInputFragment extends Fragment {
mEmailEdit.setThreshold(1); // Start working from first character
mEmailEdit.setAdapter(
- new ArrayAdapter<String>
+ new ArrayAdapter<>
(getActivity(), android.R.layout.simple_spinner_dropdown_item,
ContactHelper.getPossibleUserEmails(getActivity())
)
@@ -124,7 +124,7 @@ public class CreateKeyInputFragment extends Fragment {
mNameEdit.setThreshold(1); // Start working from first character
mNameEdit.setAdapter(
- new ArrayAdapter<String>
+ new ArrayAdapter<>
(getActivity(), android.R.layout.simple_spinner_dropdown_item,
ContactHelper.getPossibleUserNames(getActivity())
)
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
deleted file mode 100644
index 681e22e1e..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
- *
- * 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.TargetApi;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import android.view.View;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker;
-
-import java.util.regex.Matcher;
-
-public class DecryptActivity extends DrawerActivity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.decrypt_activity);
-
- activateDrawerNavigation(savedInstanceState);
-
- View actionFile = findViewById(R.id.decrypt_files);
- View actionFromClipboard = findViewById(R.id.decrypt_from_clipboard);
-
- actionFile.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent filesDecrypt = new Intent(DecryptActivity.this, DecryptFilesActivity.class);
- filesDecrypt.setAction(DecryptFilesActivity.ACTION_DECRYPT_DATA_OPEN);
- startActivity(filesDecrypt);
- }
- });
-
- actionFromClipboard.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent clipboardDecrypt = new Intent(DecryptActivity.this, DecryptTextActivity.class);
- clipboardDecrypt.setAction(DecryptTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD);
- startActivityForResult(clipboardDecrypt, 0);
- }
- });
- }
-
- @TargetApi(VERSION_CODES.HONEYCOMB)
- @Override
- protected void onResume() {
- super.onResume();
-
- // This is an eye candy ice cream sandwich feature, nvm on versions below
- if (Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
-
- // get text from clipboard
- final CharSequence clipboardText =
- ClipboardReflection.getClipboardText(DecryptActivity.this);
-
- // if it's null, nothing to do here /o/
- if (clipboardText == null) {
- return;
- }
-
- new AsyncTask<String, Void, Boolean>() {
- @Override
- protected Boolean doInBackground(String... clipboardText) {
-
- // see if it looks like a pgp thing
- Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText[0]);
- boolean animate = matcher.matches();
-
- // see if it looks like another pgp thing
- if (!animate) {
- matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText[0]);
- animate = matcher.matches();
- }
- return animate;
- }
-
- @Override
- protected void onPostExecute(Boolean animate) {
- super.onPostExecute(animate);
-
- // if so, animate the clipboard icon just a bit~
- if (animate) {
- SubtleAttentionSeeker.tada(findViewById(R.id.clipboard_icon), 1.5f).start();
- }
- }
- }.execute(clipboardText.toString());
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // if a result has been returned, display a notify
- if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
- OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
- result.createNotify(this).show();
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java
index 9d972d8c0..89dd4970b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java
@@ -20,14 +20,13 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
import org.sufficientlysecure.keychain.util.Log;
-public class DecryptFilesActivity extends ActionBarActivity {
+public class DecryptFilesActivity extends BaseActivity {
/* Intents */
public static final String ACTION_DECRYPT_DATA = OpenKeychainIntents.DECRYPT_DATA;
@@ -41,12 +40,15 @@ public class DecryptFilesActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.decrypt_files_activity);
-
// Handle intent actions
handleActions(savedInstanceState, getIntent());
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.decrypt_files_activity);
+ }
+
/**
* Handles all actions with this intent
*
@@ -86,7 +88,7 @@ public class DecryptFilesActivity extends ActionBarActivity {
loadFragment(savedInstanceState, null, true);
} else if (ACTION_DECRYPT_DATA.equals(action)) {
Log.e(Constants.TAG,
- "Include an Uri with setData() in your Intent!");
+ "Include an Uri with setInputData() in your Intent!");
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java
index 691f1c240..5606523be 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java
@@ -35,13 +35,13 @@ import android.widget.TextView;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.util.FileHelper;
+import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
@@ -310,7 +310,7 @@ public class DecryptFilesFragment extends DecryptFragment {
// A future open after decryption feature
if () {
Intent viewFile = new Intent(Intent.ACTION_VIEW);
- viewFile.setData(mOutputUri);
+ viewFile.setInputData(mOutputUri);
startActivity(viewFile);
}
*/
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
index 7f06d36e8..8723c7255 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -27,9 +27,9 @@ import android.widget.TextView;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public abstract class DecryptFragment extends Fragment {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java
index 2da76088a..81a8a2ac4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java
@@ -20,22 +20,21 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import android.text.TextUtils;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.util.Log;
import java.util.regex.Matcher;
-public class DecryptTextActivity extends ActionBarActivity {
+public class DecryptTextActivity extends BaseActivity {
/* Intents */
public static final String ACTION_DECRYPT_TEXT = OpenKeychainIntents.DECRYPT_TEXT;
@@ -50,12 +49,15 @@ public class DecryptTextActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.decrypt_text_activity);
-
// Handle intent actions
handleActions(savedInstanceState, getIntent());
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.decrypt_text_activity);
+ }
+
/**
* Fixing broken PGP MESSAGE Strings coming from GMail/AOSP Mail
*/
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
index fa7abf0f5..a15b23c06 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
@@ -34,13 +34,15 @@ import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ShareHelper;
+import java.io.UnsupportedEncodingException;
+
public class DecryptTextFragment extends DecryptFragment {
public static final String ARG_CIPHERTEXT = "ciphertext";
@@ -111,7 +113,7 @@ public class DecryptTextFragment extends DecryptFragment {
Intent prototype = createSendIntent(text);
String title = getString(R.string.title_share_file);
- // we don't want to decrypt the decypted, no inception ;)
+ // we don't want to decrypt the decrypted, no inception ;)
String[] blacklist = new String[]{
Constants.PACKAGE_NAME + ".ui.DecryptTextActivity",
"org.thialfihar.android.apg.ui.DecryptActivity"
@@ -194,7 +196,18 @@ public class DecryptTextFragment extends DecryptFragment {
byte[] decryptedMessage = returnData
.getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES);
- mText.setText(new String(decryptedMessage));
+ String displayMessage;
+ if (pgpResult.getCharset() != null) {
+ try {
+ displayMessage = new String(decryptedMessage, pgpResult.getCharset());
+ } catch (UnsupportedEncodingException e) {
+ // if we can't decode properly, just fall back to utf-8
+ displayMessage = new String(decryptedMessage);
+ }
+ } else {
+ displayMessage = new String(decryptedMessage);
+ }
+ mText.setText(displayMessage);
pgpResult.createNotify(getActivity()).show();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
deleted file mode 100644
index da46de486..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * 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;
-
-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.v4.widget.FixedDrawerLayout;
-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.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-
-public class DrawerActivity extends ActionBarActivity {
- private FixedDrawerLayout mDrawerLayout;
- private ListView mDrawerList;
- private ActionBarDrawerToggle mDrawerToggle;
-
- private CharSequence mDrawerTitle;
- private CharSequence mTitle;
- private boolean mIsDrawerLocked = false;
-
- private Class mSelectedItem;
-
- private static final int MENU_ID_PREFERENCE = 222;
- private static final int MENU_ID_HELP = 223;
-
- protected void deactivateDrawerNavigation() {
- ((DrawerLayout) findViewById(R.id.drawer_layout)).setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
- }
-
- protected void activateDrawerNavigation(Bundle savedInstanceState) {
- mDrawerTitle = getString(R.string.app_name);
- mDrawerLayout = (FixedDrawerLayout) 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;
- }
-
- NavItem mItemIconTexts[] = new NavItem[]{
- new NavItem(R.drawable.ic_action_accounts, getString(R.string.nav_keys)),
- new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_text)),
- new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_files)),
- new NavItem(R.drawable.ic_action_not_secure, getString(R.string.nav_decrypt)),
- new NavItem(R.drawable.ic_action_view_as_list, getString(R.string.nav_apps))};
-
- mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
- mItemIconTexts));
-
- mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
-
- // enable ActionBar app icon to behave as action to toggle nav drawer
- // 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 */
- ) {
- public void onDrawerClosed(View view) {
- getSupportActionBar().setTitle(mTitle);
-
- callIntentForDrawerItem(mSelectedItem);
- }
-
- public void onDrawerOpened(View drawerView) {
- mTitle = getSupportActionBar().getTitle();
- getSupportActionBar().setTitle(mDrawerTitle);
- // creates call to onPrepareOptionsMenu()
- supportInvalidateOptionsMenu();
- }
- };
-
- 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);
- }
- }
-
- /**
- * 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) {
- if (mDrawerToggle == null) {
- return super.onCreateOptionsMenu(menu);
- }
-
- menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences);
- menu.add(42, MENU_ID_HELP, 101, R.string.menu_help);
-
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (mDrawerToggle == null) {
- return super.onOptionsItemSelected(item);
- }
-
- // The action bar home/up action should open or close the drawer.
- // ActionBarDrawerToggle will take care of this.
- if (mDrawerToggle.onOptionsItemSelected(item)) {
- return true;
- }
-
- 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);
- }
- }
-
- /**
- * The click listener for ListView in the navigation drawer
- */
- private class DrawerItemClickListener implements ListView.OnItemClickListener {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- selectItem(position);
- }
- }
-
- private void selectItem(int position) {
- // update selected item and title, then close the drawer
- mDrawerList.setItemChecked(position, true);
- // set selected class
- 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);
- }
- }
-
- /**
- * When using the ActionBarDrawerToggle, you must call it during onPostCreate() and
- * onConfigurationChanged()...
- */
- @Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
- // Sync the toggle state after onRestoreInstanceState has occurred.
- if (mDrawerToggle != null) {
- mDrawerToggle.syncState();
- }
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- // Pass any configuration change to the drawer toggles
- if (mDrawerToggle != null) {
- mDrawerToggle.onConfigurationChanged(newConfig);
- }
- }
-
- private class NavItem {
- public int icon; // res-id
- public String title;
-
- /**
- * NavItem constructor
- *
- * @param icon The icons resource-id
- * @param title The title of the menu entry
- */
- public NavItem(int icon, String title) {
- super();
- this.icon = icon;
- this.title = title;
- }
- }
-
- private class NavigationDrawerAdapter extends ArrayAdapter<NavItem> {
- Context mContext;
- int mLayoutResourceId;
- NavItem mData[] = null;
-
- public NavigationDrawerAdapter(Context context, int layoutResourceId, NavItem[] data) {
- super(context, layoutResourceId, data);
- this.mLayoutResourceId = layoutResourceId;
- this.mContext = context;
- this.mData = data;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View row = convertView;
- NavItemHolder holder;
-
- if (row == null) {
- LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
- row = inflater.inflate(mLayoutResourceId, parent, false);
-
- holder = new NavItemHolder();
- holder.mImg = (ImageView) 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 = mData[position];
- holder.mTxtTitle.setText(item.title);
- holder.mImg.setImageResource(item.icon);
-
- return row;
- }
-
- }
-
- static class NavItemHolder {
- ImageView mImg;
- TextView mTxtTitle;
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index 98049d89b..6dc2994cf 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -19,14 +19,13 @@ package org.sufficientlysecure.keychain.ui;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.Log;
-public class EditKeyActivity extends ActionBarActivity {
+public class EditKeyActivity extends BaseActivity {
public static final String EXTRA_SAVE_KEYRING_PARCEL = "save_keyring_parcel";
@@ -36,8 +35,6 @@ public class EditKeyActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.edit_key_activity);
-
Uri dataUri = getIntent().getData();
SaveKeyringParcel saveKeyringParcel = getIntent().getParcelableExtra(EXTRA_SAVE_KEYRING_PARCEL);
if (dataUri == null && saveKeyringParcel == null) {
@@ -49,6 +46,11 @@ public class EditKeyActivity extends ActionBarActivity {
loadFragment(savedInstanceState, dataUri, saveKeyringParcel);
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.edit_key_activity);
+ }
+
private void loadFragment(Bundle savedInstanceState, Uri dataUri, SaveKeyringParcel saveKeyringParcel) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index afe6afb3c..25ca6e8fd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -29,7 +29,6 @@ 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.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -55,6 +54,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
@@ -65,7 +65,6 @@ import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
-import org.sufficientlysecure.keychain.ui.util.ActionBarHelper;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
@@ -147,10 +146,8 @@ public class EditKeyFragment extends LoaderFragment implements
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
-
- // Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setTwoButtonView(((ActionBarActivity) getActivity()).getSupportActionBar(),
- R.string.btn_save, R.drawable.ic_action_save,
+ ((EditKeyActivity) getActivity()).setFullScreenDialogDoneClose(
+ R.string.btn_save,
new OnClickListener() {
@Override
public void onClick(View v) {
@@ -161,16 +158,13 @@ public class EditKeyFragment extends LoaderFragment implements
saveInDatabase(mCurrentPassphrase);
}
}
- }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel,
- new OnClickListener() {
+ }, new OnClickListener() {
@Override
public void onClick(View v) {
- // cancel
getActivity().setResult(Activity.RESULT_CANCELED);
getActivity().finish();
}
- }
- );
+ });
Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
SaveKeyringParcel saveKeyringParcel = getArguments().getParcelable(ARG_SAVE_KEYRING_PARCEL);
@@ -230,10 +224,7 @@ public class EditKeyFragment extends LoaderFragment implements
mSaveKeyringParcel = new SaveKeyringParcel(masterKeyId, keyRing.getFingerprint());
mPrimaryUserId = keyRing.getPrimaryUserIdWithFallback();
- } catch (PgpKeyNotFoundException e) {
- finishWithError(LogType.MSG_EK_ERROR_NOT_FOUND);
- return;
- } catch (NotFoundException e) {
+ } catch (PgpKeyNotFoundException | NotFoundException e) {
finishWithError(LogType.MSG_EK_ERROR_NOT_FOUND);
return;
}
@@ -395,8 +386,8 @@ public class EditKeyFragment extends LoaderFragment implements
// cache new returned passphrase!
mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(
- data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
- null
+ data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
+ null
);
}
}
@@ -478,12 +469,13 @@ public class EditKeyFragment extends LoaderFragment implements
}
break;
case EditSubkeyDialogFragment.MESSAGE_STRIP:
- // toggle
- if (mSaveKeyringParcel.mStripSubKeys.contains(keyId)) {
- mSaveKeyringParcel.mStripSubKeys.remove(keyId);
- } else {
- mSaveKeyringParcel.mStripSubKeys.add(keyId);
+ SubkeyChange change = mSaveKeyringParcel.getSubkeyChange(keyId);
+ if (change == null) {
+ mSaveKeyringParcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null));
+ break;
}
+ // toggle
+ change.mDummyStrip = !change.mDummyStrip;
break;
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index b9058a37d..0d7e6056e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -8,13 +8,15 @@ import android.os.Messenger;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
+import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
+import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import java.util.Date;
-public abstract class EncryptActivity extends DrawerActivity {
+public abstract class EncryptActivity extends BaseActivity {
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
public static final int REQUEST_CODE_NFC = 0x00008002;
@@ -82,7 +84,10 @@ public abstract class EncryptActivity extends DrawerActivity {
// Send all information needed to service to edit key in other thread
Intent intent = new Intent(this, KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, createEncryptBundle());
+
+ Bundle data = new Bundle();
+ data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, createEncryptBundle());
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Message is received after encrypting is done in KeychainIntentService
KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(this,
@@ -92,28 +97,31 @@ public abstract class EncryptActivity extends DrawerActivity {
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- SignEncryptResult pgpResult =
+ SignEncryptResult result =
message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT);
- if (pgpResult.isPending()) {
- if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
- SignEncryptResult.RESULT_PENDING_PASSPHRASE) {
+ PgpSignEncryptResult pgpResult = result.getPending();
+
+ if (pgpResult != null && pgpResult.isPending()) {
+ if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
+ PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) {
startPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded());
- } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) ==
- SignEncryptResult.RESULT_PENDING_NFC) {
+ } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) ==
+ PgpSignEncryptResult.RESULT_PENDING_NFC) {
mNfcTimestamp = pgpResult.getNfcTimestamp();
- startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
+ startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(),
+ pgpResult.getNfcHash(), pgpResult.getNfcAlgo());
} else {
throw new RuntimeException("Unhandled pending result!");
}
return;
}
- if (pgpResult.success()) {
- onEncryptSuccess(message, pgpResult);
+ if (result.success()) {
+ onEncryptSuccess(result);
} else {
- pgpResult.createNotify(EncryptActivity.this).show();
+ result.createNotify(EncryptActivity.this).show();
}
// no matter the result, reset parameters
@@ -136,8 +144,8 @@ public abstract class EncryptActivity extends DrawerActivity {
protected abstract boolean inputIsValid();
- protected abstract void onEncryptSuccess(Message message, SignEncryptResult result);
+ protected abstract void onEncryptSuccess(SignEncryptResult result);
- protected abstract Bundle createEncryptBundle();
+ protected abstract SignEncryptParcel createEncryptBundle();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
index 2d1b66daa..c5404094a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -28,7 +28,6 @@ import com.tokenautocomplete.TokenCompleteTextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -164,8 +163,8 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
private void updateEncryptionKeys() {
List<Object> objects = mEncryptKeyView.getObjects();
- List<Long> keyIds = new ArrayList<Long>();
- List<String> userIds = new ArrayList<String>();
+ List<Long> keyIds = new ArrayList<>();
+ List<String> userIds = new ArrayList<>();
for (Object object : objects) {
if (object instanceof EncryptKeyCompletionView.EncryptionKey) {
keyIds.add(((EncryptKeyCompletionView.EncryptionKey) object).getKeyId());
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java
new file mode 100644
index 000000000..a498d0763
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * 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.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker;
+
+import java.util.regex.Matcher;
+
+public class EncryptDecryptOverviewFragment extends Fragment {
+
+ View mEncryptFile;
+ View mEncryptText;
+ View mDecryptFile;
+ View mDecryptFromClipboard;
+ View mClipboardIcon;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.encrypt_decrypt_overview_fragment, container, false);
+
+ mEncryptFile = view.findViewById(R.id.encrypt_files);
+ mEncryptText = view.findViewById(R.id.encrypt_text);
+ mDecryptFile = view.findViewById(R.id.decrypt_files);
+ mDecryptFromClipboard = view.findViewById(R.id.decrypt_from_clipboard);
+ mClipboardIcon = view.findViewById(R.id.clipboard_icon);
+
+ mEncryptFile.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent encrypt = new Intent(getActivity(), EncryptFilesActivity.class);
+ startActivity(encrypt);
+ }
+ });
+
+ mEncryptText.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent encrypt = new Intent(getActivity(), EncryptTextActivity.class);
+ startActivity(encrypt);
+ }
+ });
+
+ mDecryptFile.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent filesDecrypt = new Intent(getActivity(), DecryptFilesActivity.class);
+ filesDecrypt.setAction(DecryptFilesActivity.ACTION_DECRYPT_DATA_OPEN);
+ startActivity(filesDecrypt);
+ }
+ });
+
+ mDecryptFromClipboard.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent clipboardDecrypt = new Intent(getActivity(), DecryptTextActivity.class);
+ clipboardDecrypt.setAction(DecryptTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD);
+ startActivityForResult(clipboardDecrypt, 0);
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // get text from clipboard
+ final CharSequence clipboardText =
+ ClipboardReflection.getClipboardText(getActivity());
+
+ // if it's null, nothing to do here /o/
+ if (clipboardText == null) {
+ return;
+ }
+
+ new AsyncTask<String, Void, Boolean>() {
+ @Override
+ protected Boolean doInBackground(String... clipboardText) {
+
+ // see if it looks like a pgp thing
+ Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText[0]);
+ boolean animate = matcher.matches();
+
+ // see if it looks like another pgp thing
+ if (!animate) {
+ matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText[0]);
+ animate = matcher.matches();
+ }
+ return animate;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean animate) {
+ super.onPostExecute(animate);
+
+ // if so, animate the clipboard icon just a bit~
+ if (animate) {
+ SubtleAttentionSeeker.tada(mClipboardIcon, 1.5f).start();
+ }
+ }
+ }.execute(clipboardText.toString());
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // if a result has been returned, display a notify
+ if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(getActivity()).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java
index 054d85323..1286617d3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java
@@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
import android.support.v4.app.Fragment;
import android.view.Menu;
import android.view.MenuItem;
@@ -29,14 +28,14 @@ import android.view.MenuItem;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
-import org.sufficientlysecure.keychain.util.Preferences;
-import org.sufficientlysecure.keychain.util.ShareHelper;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.ShareHelper;
import java.util.ArrayList;
import java.util.HashSet;
@@ -122,13 +121,13 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
@Override
public ArrayList<Uri> getInputUris() {
- if (mInputUris == null) mInputUris = new ArrayList<Uri>();
+ if (mInputUris == null) mInputUris = new ArrayList<>();
return mInputUris;
}
@Override
public ArrayList<Uri> getOutputUris() {
- if (mOutputUris == null) mOutputUris = new ArrayList<Uri>();
+ if (mOutputUris == null) mOutputUris = new ArrayList<>();
return mOutputUris;
}
@@ -170,7 +169,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
}
@Override
- public void onEncryptSuccess(Message message, SignEncryptResult pgpResult) {
+ public void onEncryptSuccess(SignEncryptResult result) {
if (mDeleteAfterEncrypt) {
for (Uri inputUri : mInputUris) {
DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUri);
@@ -182,29 +181,25 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
if (mShareAfterEncrypt) {
// Share encrypted message/file
- startActivity(sendWithChooserExcludingEncrypt(message));
+ startActivity(sendWithChooserExcludingEncrypt());
} else {
// Save encrypted file
- pgpResult.createNotify(EncryptFilesActivity.this).show();
+ result.createNotify(EncryptFilesActivity.this).show();
}
}
@Override
- protected Bundle createEncryptBundle() {
+ protected SignEncryptParcel createEncryptBundle() {
// fill values for this action
- Bundle data = new Bundle();
+ SignEncryptParcel data = new SignEncryptParcel();
- data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URIS);
- data.putParcelableArrayList(KeychainIntentService.ENCRYPT_INPUT_URIS, mInputUris);
+ data.addInputUris(mInputUris);
+ data.addOutputUris(mOutputUris);
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URIS);
- data.putParcelableArrayList(KeychainIntentService.ENCRYPT_OUTPUT_URIS, mOutputUris);
-
- data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID,
- Preferences.getPreferences(this).getDefaultFileCompression());
+ data.setCompressionId(Preferences.getPreferences(this).getDefaultMessageCompression());
// Always use armor for messages
- data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, mUseArmor);
+ data.setEnableAsciiArmorOutput(mUseArmor);
if (isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!");
@@ -212,13 +207,12 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
if (passphrase.length() == 0) {
passphrase = null;
}
- data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
+ data.setSymmetricPassphrase(passphrase);
} else {
- data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_MASTER_ID, mSigningKeyId);
- data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds);
- data.putString(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_PASSPHRASE, mSigningKeyPassphrase);
- data.putSerializable(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_TIMESTAMP, mNfcTimestamp);
- data.putByteArray(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_HASH, mNfcHash);
+ data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
+ data.setSignatureMasterKeyId(mSigningKeyId);
+ data.setSignaturePassphrase(mSigningKeyPassphrase);
+ data.setNfcState(mNfcHash, mNfcTimestamp);
}
return data;
}
@@ -226,8 +220,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
/**
* Create Intent Chooser but exclude OK's EncryptActivity.
*/
- private Intent sendWithChooserExcludingEncrypt(Message message) {
- Intent prototype = createSendIntent(message);
+ private Intent sendWithChooserExcludingEncrypt() {
+ Intent prototype = createSendIntent();
String title = getString(R.string.title_share_file);
// we don't want to encrypt the encrypted, no inception ;)
@@ -239,7 +233,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
return new ShareHelper(this).createChooserExcluding(prototype, title, blacklist);
}
- private Intent createSendIntent(Message message) {
+ private Intent createSendIntent() {
Intent sendIntent;
// file
if (mOutputUris.size() == 1) {
@@ -252,7 +246,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
sendIntent.setType("application/octet-stream");
if (!isModeSymmetric() && mEncryptionUserIds != null) {
- Set<String> users = new HashSet<String>();
+ Set<String> users = new HashSet<>();
for (String user : mEncryptionUserIds) {
String[] userId = KeyRing.splitUserId(user);
if (userId[1] != null) {
@@ -309,15 +303,13 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.encrypt_files_activity);
-
// if called with an intent action, do not init drawer navigation
if (ACTION_ENCRYPT_DATA.equals(getIntent().getAction())) {
// lock drawer
- deactivateDrawerNavigation();
+// deactivateDrawerNavigation();
// TODO: back button to key?
} else {
- activateDrawerNavigation(savedInstanceState);
+// activateDrawerNavigation(savedInstanceState);
}
// Handle intent actions
@@ -328,6 +320,11 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
}
@Override
+ protected void initLayout() {
+ setContentView(R.layout.encrypt_files_activity);
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.encrypt_file_activity, menu);
menu.findItem(R.id.check_use_armor).setChecked(mUseArmor);
@@ -379,7 +376,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
String action = intent.getAction();
Bundle extras = intent.getExtras();
String type = intent.getType();
- ArrayList<Uri> uris = new ArrayList<Uri>();
+ ArrayList<Uri> uris = new ArrayList<>();
if (extras == null) {
extras = new Bundle();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
index 6961f5ee7..860bd8502 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
@@ -36,10 +36,10 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.FileHelper;
-import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
import java.io.File;
import java.util.HashMap;
@@ -59,7 +59,7 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt
private View mShareFile;
private ListView mSelectedFiles;
private SelectedFilesAdapter mAdapter = new SelectedFilesAdapter();
- private final Map<Uri, Bitmap> thumbnailCache = new HashMap<Uri, Bitmap>();
+ private final Map<Uri, Bitmap> thumbnailCache = new HashMap<>();
@Override
public void onAttach(Activity activity) {
@@ -224,7 +224,7 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt
@Override
public void onNotifyUpdate() {
// Clear cache if needed
- for (Uri uri : new HashSet<Uri>(thumbnailCache.keySet())) {
+ for (Uri uri : new HashSet<>(thumbnailCache.keySet())) {
if (!mEncryptInterface.getInputUris().contains(uri)) {
thumbnailCache.remove(uri);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java
index 958daa122..2dd861d07 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java
@@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
import android.support.v4.app.Fragment;
import android.view.Menu;
import android.view.MenuItem;
@@ -30,13 +29,13 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.util.Preferences;
-import org.sufficientlysecure.keychain.util.ShareHelper;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.ShareHelper;
import java.util.ArrayList;
import java.util.HashSet;
@@ -121,13 +120,13 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
@Override
public ArrayList<Uri> getInputUris() {
- if (mInputUris == null) mInputUris = new ArrayList<Uri>();
+ if (mInputUris == null) mInputUris = new ArrayList<>();
return mInputUris;
}
@Override
public ArrayList<Uri> getOutputUris() {
- if (mOutputUris == null) mOutputUris = new ArrayList<Uri>();
+ if (mOutputUris == null) mOutputUris = new ArrayList<>();
return mOutputUris;
}
@@ -169,32 +168,31 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
}
@Override
- protected void onEncryptSuccess(Message message, SignEncryptResult pgpResult) {
+ protected void onEncryptSuccess(SignEncryptResult result) {
if (mShareAfterEncrypt) {
// Share encrypted message/file
- startActivity(sendWithChooserExcludingEncrypt(message));
+ startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes()));
} else {
// Copy to clipboard
- copyToClipboard(message);
- pgpResult.createNotify(EncryptTextActivity.this).show();
+ copyToClipboard(result.getResultBytes());
+ result.createNotify(EncryptTextActivity.this).show();
// Notify.showNotify(EncryptTextActivity.this,
// R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
}
}
@Override
- protected Bundle createEncryptBundle() {
+ protected SignEncryptParcel createEncryptBundle() {
// fill values for this action
- Bundle data = new Bundle();
+ SignEncryptParcel data = new SignEncryptParcel();
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES);
- data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mMessage.getBytes());
+ data.setBytes(mMessage.getBytes());
+ data.setCleartextSignature(true);
- data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID,
- Preferences.getPreferences(this).getDefaultMessageCompression());
+ data.setCompressionId(Preferences.getPreferences(this).getDefaultMessageCompression());
// Always use armor for messages
- data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true);
+ data.setEnableAsciiArmorOutput(true);
if (isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!");
@@ -202,26 +200,25 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
if (passphrase.length() == 0) {
passphrase = null;
}
- data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
+ data.setSymmetricPassphrase(passphrase);
} else {
- data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_MASTER_ID, mSigningKeyId);
- data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds);
- data.putString(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_PASSPHRASE, mSigningKeyPassphrase);
- data.putSerializable(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_TIMESTAMP, mNfcTimestamp);
- data.putByteArray(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_HASH, mNfcHash);
+ data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
+ data.setSignatureMasterKeyId(mSigningKeyId);
+ data.setSignaturePassphrase(mSigningKeyPassphrase);
+ data.setNfcState(mNfcHash, mNfcTimestamp);
}
return data;
}
- private void copyToClipboard(Message message) {
- ClipboardReflection.copyToClipboard(this, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES)));
+ private void copyToClipboard(byte[] resultBytes) {
+ ClipboardReflection.copyToClipboard(this, new String(resultBytes));
}
/**
* Create Intent Chooser but exclude OK's EncryptActivity.
*/
- private Intent sendWithChooserExcludingEncrypt(Message message) {
- Intent prototype = createSendIntent(message);
+ private Intent sendWithChooserExcludingEncrypt(byte[] resultBytes) {
+ Intent prototype = createSendIntent(resultBytes);
String title = getString(R.string.title_share_message);
// we don't want to encrypt the encrypted, no inception ;)
@@ -233,20 +230,21 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
return new ShareHelper(this).createChooserExcluding(prototype, title, blacklist);
}
- private Intent createSendIntent(Message message) {
+ private Intent createSendIntent(byte[] resultBytes) {
Intent sendIntent;
sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("text/plain");
- sendIntent.putExtra(Intent.EXTRA_TEXT, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES)));
+ sendIntent.putExtra(Intent.EXTRA_TEXT, new String(resultBytes));
if (!isModeSymmetric() && mEncryptionUserIds != null) {
- Set<String> users = new HashSet<String>();
+ Set<String> users = new HashSet<>();
for (String user : mEncryptionUserIds) {
String[] userId = KeyRing.splitUserId(user);
if (userId[1] != null) {
users.add(userId[1]);
}
}
+ // pass trough email addresses as extra for email applications
sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()]));
}
return sendIntent;
@@ -288,15 +286,13 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.encrypt_text_activity);
-
// if called with an intent action, do not init drawer navigation
if (ACTION_ENCRYPT_TEXT.equals(getIntent().getAction())) {
// lock drawer
- deactivateDrawerNavigation();
+// deactivateDrawerNavigation();
// TODO: back button to key?
} else {
- activateDrawerNavigation(savedInstanceState);
+// activateDrawerNavigation(savedInstanceState);
}
// Handle intent actions
@@ -305,6 +301,11 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
}
@Override
+ protected void initLayout() {
+ setContentView(R.layout.encrypt_text_activity);
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.encrypt_text_activity, menu);
return super.onCreateOptionsMenu(menu);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
index d71c9c465..393e15cfa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
@@ -19,17 +19,16 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
-public class FirstTimeActivity extends ActionBarActivity {
+public class FirstTimeActivity extends BaseActivity {
View mCreateKey;
View mImportKey;
@@ -43,8 +42,6 @@ public class FirstTimeActivity extends ActionBarActivity {
super.onCreate(savedInstanceState);
- setContentView(R.layout.first_time_activity);
-
mCreateKey = findViewById(R.id.first_time_create_key);
mImportKey = findViewById(R.id.first_time_import_key);
mSkipSetup = findViewById(R.id.first_time_cancel);
@@ -72,7 +69,11 @@ public class FirstTimeActivity extends ActionBarActivity {
startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
}
});
+ }
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.first_time_activity);
}
@Override
@@ -91,7 +92,7 @@ public class FirstTimeActivity extends ActionBarActivity {
private void finishSetup(Intent srcData) {
Preferences prefs = Preferences.getPreferences(this);
prefs.setFirstTime(false);
- Intent intent = new Intent(this, KeyListActivity.class);
+ Intent intent = new Intent(this, MainActivity.class);
// give intent through to display notify
if (srcData != null) {
intent.putExtras(srcData);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
index bbc1e4b1f..2eb35351e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
@@ -20,14 +20,14 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
+import android.view.View;
+
+import com.astuetz.PagerSlidingTabStrip;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
-import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
-public class HelpActivity extends ActionBarActivity {
+public class HelpActivity extends BaseActivity {
public static final String EXTRA_SELECTED_TAB = "selected_tab";
public static final int TAB_START = 0;
@@ -44,16 +44,16 @@ public class HelpActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(false);
- actionBar.setHomeButtonEnabled(false);
-
- setContentView(R.layout.help_activity);
+ mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
mViewPager = (ViewPager) findViewById(R.id.pager);
- SlidingTabLayout slidingTabLayout =
- (SlidingTabLayout) findViewById(R.id.sliding_tab_layout);
+ PagerSlidingTabStrip slidingTabLayout =
+ (PagerSlidingTabStrip) findViewById(R.id.sliding_tab_layout);
mTabsAdapter = new PagerTabStripAdapter(this);
mViewPager.setAdapter(mTabsAdapter);
@@ -98,4 +98,9 @@ public class HelpActivity extends ActionBarActivity {
// switch to tab selected by extra
mViewPager.setCurrentItem(selectedTab);
}
+
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.help_activity);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index dfb7b3056..71f6fd4bf 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -17,19 +17,13 @@
package org.sufficientlysecure.keychain.ui;
-import android.annotation.TargetApi;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
-import android.nfc.NdefMessage;
-import android.nfc.NfcAdapter;
-import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
-import android.os.Parcelable;
import android.support.v4.app.Fragment;
-import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
@@ -51,7 +45,7 @@ import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize
import java.io.IOException;
import java.util.ArrayList;
-public class ImportKeysActivity extends ActionBarActivity {
+public class ImportKeysActivity extends BaseActivity {
public static final String ACTION_IMPORT_KEY = OpenKeychainIntents.IMPORT_KEY;
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER;
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT =
@@ -64,9 +58,6 @@ public class ImportKeysActivity extends ActionBarActivity {
// Actions for internal use only:
public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_FILE";
- public static final String ACTION_IMPORT_KEY_FROM_NFC = Constants.INTENT_PREFIX
- + "IMPORT_KEY_FROM_NFC";
-
public static final String EXTRA_RESULT = "result";
// only used by ACTION_IMPORT_KEY
@@ -90,8 +81,6 @@ public class ImportKeysActivity extends ActionBarActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.import_keys_activity);
-
mImportButton = findViewById(R.id.import_import);
mImportButton.setOnClickListener(new OnClickListener() {
@Override
@@ -103,6 +92,11 @@ public class ImportKeysActivity extends ActionBarActivity {
handleActions(savedInstanceState, getIntent());
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.import_keys_activity);
+ }
+
protected void handleActions(Bundle savedInstanceState, Intent intent) {
String action = intent.getAction();
Bundle extras = intent.getExtras();
@@ -113,105 +107,114 @@ public class ImportKeysActivity extends ActionBarActivity {
extras = new Bundle();
}
+ if (action == null) {
+ startCloudFragment(savedInstanceState, null, false);
+ startListFragment(savedInstanceState, null, null, null);
+ return;
+ }
+
if (Intent.ACTION_VIEW.equals(action)) {
// Android's Action when opening file associated to Keychain (see AndroidManifest.xml)
// delegate action to ACTION_IMPORT_KEY
action = ACTION_IMPORT_KEY;
}
- if (ACTION_IMPORT_KEY.equals(action)) {
- /* Keychain's own Actions */
- startFileFragment(savedInstanceState);
+ switch (action) {
+ case ACTION_IMPORT_KEY: {
+ /* Keychain's own Actions */
+ startFileFragment(savedInstanceState);
- if (dataUri != null) {
- // action: directly load data
- startListFragment(savedInstanceState, null, dataUri, null);
- } else if (extras.containsKey(EXTRA_KEY_BYTES)) {
- byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES);
+ if (dataUri != null) {
+ // action: directly load data
+ startListFragment(savedInstanceState, null, dataUri, null);
+ } else if (extras.containsKey(EXTRA_KEY_BYTES)) {
+ byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES);
- // action: directly load data
- startListFragment(savedInstanceState, importData, null, null);
+ // action: directly load data
+ startListFragment(savedInstanceState, importData, null, null);
+ }
+ break;
}
- } else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)
- || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(action)
- || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(action)) {
+ case ACTION_IMPORT_KEY_FROM_KEYSERVER:
+ case ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE:
+ case ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT: {
- // only used for OpenPgpService
- if (extras.containsKey(EXTRA_PENDING_INTENT_DATA)) {
- mPendingIntentData = extras.getParcelable(EXTRA_PENDING_INTENT_DATA);
- }
- if (extras.containsKey(EXTRA_QUERY) || extras.containsKey(EXTRA_KEY_ID)) {
- /* simple search based on query or key id */
-
- String query = null;
- if (extras.containsKey(EXTRA_QUERY)) {
- query = extras.getString(EXTRA_QUERY);
- } else if (extras.containsKey(EXTRA_KEY_ID)) {
- long keyId = extras.getLong(EXTRA_KEY_ID, 0);
- if (keyId != 0) {
- query = KeyFormattingUtils.convertKeyIdToHex(keyId);
- }
+ // only used for OpenPgpService
+ if (extras.containsKey(EXTRA_PENDING_INTENT_DATA)) {
+ mPendingIntentData = extras.getParcelable(EXTRA_PENDING_INTENT_DATA);
}
+ if (extras.containsKey(EXTRA_QUERY) || extras.containsKey(EXTRA_KEY_ID)) {
+ /* simple search based on query or key id */
+
+ String query = null;
+ if (extras.containsKey(EXTRA_QUERY)) {
+ query = extras.getString(EXTRA_QUERY);
+ } else if (extras.containsKey(EXTRA_KEY_ID)) {
+ long keyId = extras.getLong(EXTRA_KEY_ID, 0);
+ if (keyId != 0) {
+ query = KeyFormattingUtils.convertKeyIdToHex(keyId);
+ }
+ }
- if (query != null && query.length() > 0) {
- // display keyserver fragment with query
- startCloudFragment(savedInstanceState, query, false);
+ if (query != null && query.length() > 0) {
+ // display keyserver fragment with query
+ startCloudFragment(savedInstanceState, query, false);
- // action: search immediately
- startListFragment(savedInstanceState, null, null, query);
- } else {
- Log.e(Constants.TAG, "Query is empty!");
- return;
- }
- } else if (extras.containsKey(EXTRA_FINGERPRINT)) {
- /*
- * search based on fingerprint, here we can enforce a check in the end
- * if the right key has been downloaded
- */
+ // action: search immediately
+ startListFragment(savedInstanceState, null, null, query);
+ } else {
+ Log.e(Constants.TAG, "Query is empty!");
+ return;
+ }
+ } else if (extras.containsKey(EXTRA_FINGERPRINT)) {
+ /*
+ * search based on fingerprint, here we can enforce a check in the end
+ * if the right key has been downloaded
+ */
- String fingerprint = extras.getString(EXTRA_FINGERPRINT);
- if (isFingerprintValid(fingerprint)) {
- String query = "0x" + fingerprint;
+ String fingerprint = extras.getString(EXTRA_FINGERPRINT);
+ if (isFingerprintValid(fingerprint)) {
+ String query = "0x" + fingerprint;
- // display keyserver fragment with query
- startCloudFragment(savedInstanceState, query, true);
+ // display keyserver fragment with query
+ startCloudFragment(savedInstanceState, query, true);
- // action: search immediately
- startListFragment(savedInstanceState, null, null, query);
+ // action: search immediately
+ startListFragment(savedInstanceState, null, null, query);
+ }
+ } else {
+ Log.e(Constants.TAG,
+ "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or " +
+ "'fingerprint' extra!"
+ );
+ return;
}
- } else {
- Log.e(Constants.TAG,
- "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or " +
- "'fingerprint' extra!"
- );
- return;
+ break;
}
- } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
- // NOTE: this only displays the appropriate fragment, no actions are taken
- startFileFragment(savedInstanceState);
-
- // no immediate actions!
- startListFragment(savedInstanceState, null, null, null);
- } else if (ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(action)) {
- // NOTE: this only displays the appropriate fragment, no actions are taken
- startFileFragment(savedInstanceState);
+ case ACTION_IMPORT_KEY_FROM_FILE: {
+ // NOTE: this only displays the appropriate fragment, no actions are taken
+ startFileFragment(savedInstanceState);
- // no immediate actions!
- startListFragment(savedInstanceState, null, null, null);
- } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) {
- // NOTE: this only displays the appropriate fragment, no actions are taken
- startFileFragment(savedInstanceState);
- // TODO!!!!!
+ // no immediate actions!
+ startListFragment(savedInstanceState, null, null, null);
+ break;
+ }
+ case ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN: {
+ // NOTE: this only displays the appropriate fragment, no actions are taken
+ startFileFragment(savedInstanceState);
- // no immediate actions!
- startListFragment(savedInstanceState, null, null, null);
- } else {
- startCloudFragment(savedInstanceState, null, false);
- startListFragment(savedInstanceState, null, null, null);
+ // no immediate actions!
+ startListFragment(savedInstanceState, null, null, null);
+ break;
+ }
+ default: {
+ startCloudFragment(savedInstanceState, null, false);
+ startListFragment(savedInstanceState, null, null, null);
+ break;
+ }
}
}
-
private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
@@ -353,7 +356,7 @@ public class ImportKeysActivity extends ActionBarActivity {
// We parcel this iteratively into a file - anything we can
// display here, we should be able to import.
ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl");
+ new ParcelableFileCache<>(this, "key_import.pcl");
cache.writeCache(selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@@ -385,7 +388,7 @@ public class ImportKeysActivity extends ActionBarActivity {
data.putString(KeychainIntentService.IMPORT_KEY_SERVER, sls.mCloudPrefs.keyserver);
// get selected key entries
- ArrayList<ParcelableKeyRing> keys = new ArrayList<ParcelableKeyRing>();
+ ArrayList<ParcelableKeyRing> keys = new ArrayList<>();
{
// change the format into ParcelableKeyRing
ArrayList<ImportKeysListEntry> entries = mListFragment.getSelectedEntries();
@@ -413,50 +416,4 @@ public class ImportKeysActivity extends ActionBarActivity {
}
}
- /**
- * NFC
- */
- @Override
- public void onResume() {
- super.onResume();
-
- // Check to see if the Activity started due to an Android Beam
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
- handleActionNdefDiscovered(getIntent());
- } else {
- Log.d(Constants.TAG, "NFC: No NDEF discovered!");
- }
- } else {
- Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1");
- }
- }
-
- /**
- * NFC
- */
- @Override
- public void onNewIntent(Intent intent) {
- // onResume gets called after this to handle the intent
- setIntent(intent);
- }
-
- /**
- * NFC: Parses the NDEF Message from the intent and prints to the TextView
- */
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- void handleActionNdefDiscovered(Intent intent) {
- Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
- // only one message sent during the beam
- NdefMessage msg = (NdefMessage) rawMsgs[0];
- // record 0 contains the MIME type, record 1 is the AAR, if present
- byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
-
- Intent importIntent = new Intent(this, ImportKeysActivity.class);
- importIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY);
- importIntent.putExtra(ImportKeysActivity.EXTRA_KEY_BYTES, receivedKeyringBytes);
-
- handleActions(null, importIntent);
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java
index 03aba344a..91ca93c36 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java
@@ -21,6 +21,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.preference.PreferenceActivity;
import android.support.v4.app.Fragment;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -36,8 +37,8 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.ContactHelper;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
import java.util.List;
@@ -81,7 +82,7 @@ public class ImportKeysCloudFragment extends Fragment {
namesAndEmails.addAll(ContactHelper.getContactMails(getActivity()));
mQueryEditText.setThreshold(3);
mQueryEditText.setAdapter(
- new ArrayAdapter<String>
+ new ArrayAdapter<>
(getActivity(), android.R.layout.simple_spinner_dropdown_item,
namesAndEmails
)
@@ -110,11 +111,9 @@ public class ImportKeysCloudFragment extends Fragment {
mConfigButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- Intent i = new Intent(mImportActivity, PreferencesActivity.class);
- // GRR, for some reason I can’t set the Action or I get an incomprehensible
- // exception about “modern two-pane layouts”
- // i.setAction(PreferencesActivity.ACTION_PREFS_CLOUD);
- startActivity(i);
+ Intent intent = new Intent(mImportActivity, SettingsActivity.class);
+ intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, SettingsActivity.CloudSearchPrefsFragment.class.getName());
+ startActivity(intent);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index 4fe53fb09..6a6140892 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -113,7 +113,7 @@ public class ImportKeysListFragment extends ListFragment implements
return mAdapter.getSelectedEntries();
} else {
Log.e(Constants.TAG, "Adapter not initialized, returning empty list");
- return new ArrayList<ImportKeysListEntry>();
+ return new ArrayList<>();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
index 5966870df..948da94d8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
@@ -17,12 +17,17 @@
package org.sufficientlysecure.keychain.ui;
+import android.annotation.TargetApi;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
+import android.nfc.NdefMessage;
+import android.nfc.NfcAdapter;
+import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
+import android.os.Parcelable;
import android.support.v4.app.FragmentActivity;
import android.widget.Toast;
@@ -48,10 +53,13 @@ import java.util.Locale;
/**
* Proxy activity (just a transparent content view) to scan QR Codes using the Barcode Scanner app
*/
-public class QrCodeScanActivity extends FragmentActivity {
+public class ImportKeysProxyActivity extends FragmentActivity {
public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE;
public static final String ACTION_SCAN_WITH_RESULT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_WITH_RESULT";
+ public static final String ACTION_SCAN_IMPORT = Constants.INTENT_PREFIX + "SCAN_QR_CODE_IMPORT";
+
+ public static final String EXTRA_FINGERPRINT = "fingerprint";
boolean returnResult;
@@ -73,17 +81,35 @@ public class QrCodeScanActivity extends FragmentActivity {
// Scanning a fingerprint directly with Barcode Scanner, thus we already have scanned
returnResult = false;
- startCertify(dataUri);
+ processScannedContent(dataUri);
+ } else if (ACTION_SCAN_IMPORT.equals(action)) {
+ returnResult = false;
+ IntentIntegrator integrator = new IntentIntegrator(this);
+ integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
+ .setPrompt(getString(R.string.import_qr_code_text))
+ .setResultDisplayDuration(0)
+ .initiateScan();
} else if (ACTION_SCAN_WITH_RESULT.equals(action)) {
- // scan using xzing's Barcode Scanner and return result parcel in OpenKeychain
-
returnResult = true;
- new IntentIntegrator(this).initiateScan();
+ IntentIntegrator integrator = new IntentIntegrator(this);
+ integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
+ .setPrompt(getString(R.string.import_qr_code_text))
+ .setResultDisplayDuration(0)
+ .initiateScan();
} else if (ACTION_QR_CODE_API.equals(action)) {
// scan using xzing's Barcode Scanner from outside OpenKeychain
returnResult = false;
new IntentIntegrator(this).initiateScan();
+ } else if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
+ // Check to see if the Activity started due to an Android Beam
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ returnResult = false;
+ handleActionNdefDiscovered(getIntent());
+ } else {
+ Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1");
+ finish();
+ }
} else {
Log.e(Constants.TAG, "No valid scheme or action given!");
finish();
@@ -95,16 +121,16 @@ public class QrCodeScanActivity extends FragmentActivity {
if (requestCode == 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);
- startCertify(Uri.parse(scanResult.getContents()));
- } else {
+ if (scanResult == null || scanResult.getFormatName() == null) {
Log.e(Constants.TAG, "scanResult or formatName null! Should not happen!");
finish();
+ return;
}
+ String scannedContent = scanResult.getContents();
+ processScannedContent(scannedContent);
+
return;
}
// if a result has been returned, return it down to other activity
@@ -112,6 +138,42 @@ public class QrCodeScanActivity extends FragmentActivity {
returnResult(data);
} else {
super.onActivityResult(requestCode, resultCode, data);
+ finish();
+ }
+ }
+
+ private void processScannedContent(String content) {
+ Uri uri = Uri.parse(content);
+ processScannedContent(uri);
+ }
+
+ private void processScannedContent(Uri uri) {
+
+ Log.d(Constants.TAG, "scanned: " + uri);
+
+ String fingerprint = null;
+
+ // example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282
+ if (uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
+ fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
+ }
+
+ if (fingerprint == null) {
+ SingletonResult result = new SingletonResult(
+ SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_WRONG_QR_CODE);
+ Intent intent = new Intent();
+ intent.putExtra(SingletonResult.EXTRA_RESULT, result);
+ returnResult(intent);
+ return;
+ }
+
+ if (returnResult) {
+ Intent result = new Intent();
+ result.putExtra(EXTRA_FINGERPRINT, fingerprint);
+ setResult(RESULT_OK, result);
+ finish();
+ } else {
+ importKeys(fingerprint);
}
}
@@ -128,21 +190,28 @@ public class QrCodeScanActivity extends FragmentActivity {
}
}
- public void startCertify(Uri dataUri) {
- // example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282
- if (dataUri.getScheme().equals(Constants.FINGERPRINT_SCHEME)) {
- String fingerprint = dataUri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
- importKeys(fingerprint);
- } else {
- SingletonResult result = new SingletonResult(
- SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_WRONG_QR_CODE);
- Intent intent = new Intent();
- intent.putExtra(SingletonResult.EXTRA_RESULT, result);
- returnResult(intent);
- }
+ public void importKeys(byte[] keyringData) {
+
+ ParcelableKeyRing keyEntry = new ParcelableKeyRing(keyringData);
+ ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
+ selectedEntries.add(keyEntry);
+
+ startImportService(selectedEntries);
+
}
public void importKeys(String fingerprint) {
+
+ ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
+ ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
+ selectedEntries.add(keyEntry);
+
+ startImportService(selectedEntries);
+
+ }
+
+ private void startImportService (ArrayList<ParcelableKeyRing> keyRings) {
+
// Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(
this,
@@ -168,7 +237,7 @@ public class QrCodeScanActivity extends FragmentActivity {
return;
}
- if ( ! result.success()) {
+ if (!result.success()) {
// only return if no success...
Intent data = new Intent();
data.putExtras(returnData);
@@ -176,34 +245,32 @@ public class QrCodeScanActivity extends FragmentActivity {
return;
}
- Intent certifyIntent = new Intent(QrCodeScanActivity.this, CertifyKeyActivity.class);
+ Intent certifyIntent = new Intent(ImportKeysProxyActivity.this,
+ CertifyKeyActivity.class);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS,
+ result.getImportedMasterKeyIds());
startActivityForResult(certifyIntent, 0);
}
}
};
- // search config
- Preferences prefs = Preferences.getPreferences(this);
- Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
-
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
-
// fill values for this action
Bundle data = new Bundle();
- data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
-
- ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
- ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<ParcelableKeyRing>();
- selectedEntries.add(keyEntry);
+ // search config
+ {
+ Preferences prefs = Preferences.getPreferences(this);
+ Preferences.CloudSearchPrefs cloudPrefs =
+ new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
+ data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ }
- data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries);
+ data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, keyRings);
+ // Send all information needed to service to query keys in other thread
+ Intent intent = new Intent(this, KeychainIntentService.class);
+ intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
@@ -215,6 +282,20 @@ public class QrCodeScanActivity extends FragmentActivity {
// start service with intent
startService(intent);
+
+ }
+
+ /**
+ * NFC: Parses the NDEF Message from the intent and prints to the TextView
+ */
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ void handleActionNdefDiscovered(Intent intent) {
+ Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
+ // only one message sent during the beam
+ NdefMessage msg = (NdefMessage) rawMsgs[0];
+ // record 0 contains the MIME type, record 1 is the AAR, if present
+ byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
+ importKeys(receivedKeyringBytes);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
deleted file mode 100644
index 8d26cc955..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
- *
- * 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.app.ProgressDialog;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.Menu;
-import android.view.MenuItem;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.ui.util.Notify;
-import org.sufficientlysecure.keychain.util.ExportHelper;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.Preferences;
-
-import java.io.IOException;
-
-public class KeyListActivity extends DrawerActivity {
-
- public static final int REQUEST_CODE_RESULT_TO_LIST = 1;
-
- ExportHelper mExportHelper;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setTitle(R.string.nav_keys);
-
- // if this is the first time show first time activity
- Preferences prefs = Preferences.getPreferences(this);
- if (prefs.isFirstTime()) {
- startActivity(new Intent(this, FirstTimeActivity.class));
- finish();
- return;
- }
-
- mExportHelper = new ExportHelper(this);
-
- setContentView(R.layout.key_list_activity);
-
- Intent data = getIntent();
- // If we got an EXTRA_RESULT in the intent, show the notification
- if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
- OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
- result.createNotify(this).show();
- }
-
- // now setup navigation drawer in DrawerActivity...
- activateDrawerNavigation(savedInstanceState);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.key_list, menu);
-
- if (Constants.DEBUG) {
- menu.findItem(R.id.menu_key_list_debug_cons).setVisible(true);
- menu.findItem(R.id.menu_key_list_debug_read).setVisible(true);
- menu.findItem(R.id.menu_key_list_debug_write).setVisible(true);
- menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true);
- }
-
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_key_list_add:
- Intent scanQrCode = new Intent(this, QrCodeScanActivity.class);
- scanQrCode.setAction(QrCodeScanActivity.ACTION_SCAN_WITH_RESULT);
- startActivityForResult(scanQrCode, 0);
- return true;
-
- case R.id.menu_key_list_search_cloud:
- searchCloud();
- return true;
-
- case R.id.menu_key_list_create:
- createKey();
- return true;
-
- case R.id.menu_key_list_import_existing_key:
- Intent intentImportExisting = new Intent(this, ImportKeysActivity.class);
- intentImportExisting.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
- startActivityForResult(intentImportExisting, 0);
- return true;
-
- case R.id.menu_key_list_export:
- mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true);
- return true;
-
- case R.id.menu_key_list_debug_cons:
- consolidate();
- return true;
-
- case R.id.menu_key_list_debug_read:
- try {
- KeychainDatabase.debugBackup(this, true);
- Notify.showNotify(this, "Restored debug_backup.db", Notify.Style.INFO);
- getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null);
- } catch (IOException e) {
- Log.e(Constants.TAG, "IO Error", e);
- Notify.showNotify(this, "IO Error " + e.getMessage(), Notify.Style.ERROR);
- }
- return true;
-
- case R.id.menu_key_list_debug_write:
- try {
- KeychainDatabase.debugBackup(this, false);
- Notify.showNotify(this, "Backup to debug_backup.db completed", Notify.Style.INFO);
- } catch (IOException e) {
- Log.e(Constants.TAG, "IO Error", e);
- Notify.showNotify(this, "IO Error: " + e.getMessage(), Notify.Style.ERROR);
- }
- return true;
-
- case R.id.menu_key_list_debug_first_time:
- Preferences prefs = Preferences.getPreferences(this);
- prefs.setFirstTime(true);
- Intent intent = new Intent(this, FirstTimeActivity.class);
- startActivity(intent);
- finish();
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- private void searchCloud() {
- Intent importIntent = new Intent(this, ImportKeysActivity.class);
- importIntent.putExtra(ImportKeysActivity.EXTRA_QUERY, (String) null); // hack to show only cloud tab
- startActivity(importIntent);
- }
-
- private void createKey() {
- Intent intent = new Intent(this, CreateKeyActivity.class);
- startActivityForResult(intent, 0);
- }
-
- private void consolidate() {
- // 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();
- if (returnData == null) {
- return;
- }
- final ConsolidateResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- return;
- }
-
- result.createNotify(KeyListActivity.this).show();
- }
- }
- };
-
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // if a result has been returned, display a notify
- if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
- OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
- result.createNotify(this).show();
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index d0be052d8..b56da463a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
+import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.ProgressDialog;
import android.content.Context;
@@ -31,62 +32,54 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
-import android.support.v4.app.FragmentActivity;
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.v4.widget.CursorAdapter;
-import android.support.v4.widget.NoScrollableSwipeRefreshLayout;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.MotionEvent;
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.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+import com.getbase.floatingactionbutton.FloatingActionButton;
+import com.getbase.floatingactionbutton.FloatingActionsMenu;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.ExportHelper;
-import org.sufficientlysecure.keychain.util.KeyUpdateHelper;
import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
-import org.sufficientlysecure.keychain.ui.widget.ListAwareSwipeRefreshLayout;
import org.sufficientlysecure.keychain.ui.util.Highlighter;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache;
+import org.sufficientlysecure.keychain.util.ExportHelper;
+import org.sufficientlysecure.keychain.util.FabContainer;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
-import edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
-import edu.cmu.cylab.starslinger.exchange.ExchangeConfig;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
@@ -96,21 +89,26 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
*/
public class KeyListFragment extends LoaderFragment
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
- LoaderManager.LoaderCallbacks<Cursor> {
+ LoaderManager.LoaderCallbacks<Cursor>, FabContainer {
+
+ ExportHelper mExportHelper;
private KeyListAdapter mAdapter;
private StickyListHeadersListView mStickyList;
- private ListAwareSwipeRefreshLayout mSwipeRefreshLayout;
// saves the mode object for multiselect, needed for reset at some point
private ActionMode mActionMode = null;
- private boolean mShowAllKeys = true;
-
private String mQuery;
- private SearchView mSearchView;
- boolean hideMenu = false;
+ private FloatingActionsMenu mFab;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mExportHelper = new ExportHelper(getActivity());
+ }
/**
* Load custom layout with StickyListView from library
@@ -123,71 +121,36 @@ public class KeyListFragment extends LoaderFragment
mStickyList = (StickyListHeadersListView) view.findViewById(R.id.key_list_list);
mStickyList.setOnItemClickListener(this);
- mSwipeRefreshLayout = (ListAwareSwipeRefreshLayout) view.findViewById(R.id.key_list_swipe_container);
- mSwipeRefreshLayout.setOnRefreshListener(new NoScrollableSwipeRefreshLayout.OnRefreshListener() {
+ mFab = (FloatingActionsMenu) view.findViewById(R.id.fab_main);
+
+ FloatingActionButton fabQrCode = (FloatingActionButton) view.findViewById(R.id.fab_add_qr_code);
+ FloatingActionButton fabCloud = (FloatingActionButton) view.findViewById(R.id.fab_add_cloud);
+ FloatingActionButton fabFile = (FloatingActionButton) view.findViewById(R.id.fab_add_file);
+
+ fabQrCode.setOnClickListener(new OnClickListener() {
@Override
- public void onRefresh() {
- KeychainIntentServiceHandler finishedHandler = new KeychainIntentServiceHandler(getActivity()) {
- public void handleMessage(Message message) {
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- mSwipeRefreshLayout.setRefreshing(false);
- }
- }
- };
- // new KeyUpdateHelper().updateAllKeys(getActivity(), finishedHandler);
- updateActionbarForSwipe(false);
+ public void onClick(View v) {
+ mFab.collapse();
+ scanQrCode();
}
});
- mSwipeRefreshLayout.setColorScheme(
- R.color.android_purple_dark,
- R.color.android_purple_light,
- R.color.android_purple_dark,
- R.color.android_purple_light);
- mSwipeRefreshLayout.setStickyListHeadersListView(mStickyList);
- mSwipeRefreshLayout.setOnTouchListener(new View.OnTouchListener() {
+ fabCloud.setOnClickListener(new OnClickListener() {
@Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
- updateActionbarForSwipe(true);
- } else {
- updateActionbarForSwipe(false);
- }
- return false;
+ public void onClick(View v) {
+ mFab.collapse();
+ searchCloud();
+ }
+ });
+ fabFile.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mFab.collapse();
+ importFile();
}
});
- // Just disable for now
- mSwipeRefreshLayout.setIsLocked(true);
- return root;
- }
- private void updateActionbarForSwipe(boolean show) {
- ActionBarActivity activity = (ActionBarActivity) getActivity();
- ActionBar bar = activity.getSupportActionBar();
-
- if (show) {
- bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
- bar.setDisplayUseLogoEnabled(false);
- bar.setCustomView(R.layout.custom_actionbar);
- TextView title = (TextView) getActivity().findViewById(R.id.custom_actionbar_text);
- title.setText(R.string.swipe_to_update);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- hideMenu = true;
- activity.invalidateOptionsMenu();
- }
- } else {
- bar.setTitle(getActivity().getTitle());
- bar.setDisplayHomeAsUpEnabled(true);
- bar.setDisplayShowTitleEnabled(true);
- bar.setDisplayUseLogoEnabled(true);
- bar.setDisplayShowHomeEnabled(true);
- bar.setDisplayShowCustomEnabled(false);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- hideMenu = false;
- activity.invalidateOptionsMenu();
- }
- }
+ return root;
}
/**
@@ -198,90 +161,91 @@ public class KeyListFragment extends LoaderFragment
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ // show app name instead of "keys" from nav drawer
+ getActivity().setTitle(R.string.app_name);
+
mStickyList.setOnItemClickListener(this);
mStickyList.setAreHeadersSticky(true);
mStickyList.setDrawingListUnderStickyHeader(false);
mStickyList.setFastScrollEnabled(true);
/*
- * Multi-selection is only available for Android >= 3.0
+ * Multi-selection
*/
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- mStickyList.setFastScrollAlwaysVisible(true);
+ mStickyList.setFastScrollAlwaysVisible(true);
- mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
- mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
+ 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);
- mActionMode = mode;
- return true;
- }
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ android.view.MenuInflater inflater = getActivity().getMenuInflater();
+ inflater.inflate(R.menu.key_list_multi, menu);
+ mActionMode = mode;
+ return true;
+ }
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- // get IDs for checked positions as long array
- long[] ids;
+ // get IDs for checked positions as long array
+ long[] ids;
- switch (item.getItemId()) {
- case R.id.menu_key_list_multi_encrypt: {
- ids = mAdapter.getCurrentSelectedMasterKeyIds();
- encrypt(mode, ids);
- break;
- }
- case R.id.menu_key_list_multi_delete: {
- ids = mAdapter.getCurrentSelectedMasterKeyIds();
- showDeleteKeyDialog(mode, ids, mAdapter.isAnySecretSelected());
- break;
- }
- case R.id.menu_key_list_multi_export: {
- ids = mAdapter.getCurrentSelectedMasterKeyIds();
- ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
- mExportHelper.showExportKeysDialog(ids, Constants.Path.APP_DIR_FILE,
- mAdapter.isAnySecretSelected());
- 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;
+ switch (item.getItemId()) {
+ case R.id.menu_key_list_multi_encrypt: {
+ ids = mAdapter.getCurrentSelectedMasterKeyIds();
+ encrypt(mode, ids);
+ break;
+ }
+ case R.id.menu_key_list_multi_delete: {
+ ids = mAdapter.getCurrentSelectedMasterKeyIds();
+ showDeleteKeyDialog(mode, ids, mAdapter.isAnySecretSelected());
+ break;
+ }
+ case R.id.menu_key_list_multi_export: {
+ ids = mAdapter.getCurrentSelectedMasterKeyIds();
+ ExportHelper mExportHelper = new ExportHelper(getActivity());
+ mExportHelper.showExportKeysDialog(ids, Constants.Path.APP_DIR_FILE,
+ mAdapter.isAnySecretSelected());
+ 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;
}
+ return true;
+ }
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- mActionMode = null;
- mAdapter.clearSelection();
- }
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ mActionMode = null;
+ mAdapter.clearSelection();
+ }
- @Override
- public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
- boolean checked) {
- if (checked) {
- mAdapter.setNewSelection(position, checked);
- } else {
- mAdapter.removeSelection(position);
- }
- int count = mStickyList.getCheckedItemCount();
- String keysSelected = getResources().getQuantityString(
- R.plurals.key_list_selected_keys, count, count);
- mode.setTitle(keysSelected);
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
+ boolean checked) {
+ if (checked) {
+ mAdapter.setNewSelection(position, true);
+ } else {
+ mAdapter.removeSelection(position);
}
+ int count = mStickyList.getCheckedItemCount();
+ String keysSelected = getResources().getQuantityString(
+ R.plurals.key_list_selected_keys, count, count);
+ mode.setTitle(keysSelected);
+ }
- });
- }
+ });
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
@@ -340,14 +304,6 @@ public class KeyListFragment extends LoaderFragment
whereArgs[i] = "%" + words[i] + "%";
}
}
- if (!mShowAllKeys) {
- if (where == null) {
- where = "";
- } else {
- where += " AND ";
- }
- where += KeyRings.VERIFIED + " != 0";
- }
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
@@ -368,9 +324,7 @@ public class KeyListFragment extends LoaderFragment
// end action mode, if any
if (mActionMode != null) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- mActionMode.finish();
- }
+ mActionMode.finish();
}
// The list should now be shown.
@@ -400,7 +354,6 @@ public class KeyListFragment extends LoaderFragment
startActivity(viewIntent);
}
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
protected void encrypt(ActionMode mode, long[] masterKeyIds) {
Intent intent = new Intent(getActivity(), EncryptFilesActivity.class);
intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA);
@@ -414,10 +367,8 @@ public class KeyListFragment extends LoaderFragment
/**
* Show dialog to delete key
*
- * @param masterKeyIds
* @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not
*/
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {
// Can only work on singular secret keys
if (hasSecret && masterKeyIds.length > 1) {
@@ -455,25 +406,27 @@ public class KeyListFragment extends LoaderFragment
@Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+ inflater.inflate(R.menu.key_list, menu);
+
+ if (Constants.DEBUG) {
+ menu.findItem(R.id.menu_key_list_debug_cons).setVisible(true);
+ menu.findItem(R.id.menu_key_list_debug_read).setVisible(true);
+ menu.findItem(R.id.menu_key_list_debug_write).setVisible(true);
+ menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true);
+ }
+
// Get the searchview
MenuItem searchItem = menu.findItem(R.id.menu_key_list_search);
- mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
+ SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
// Execute this when searching
- mSearchView.setOnQueryTextListener(this);
-
- View searchPlate = mSearchView.findViewById(android.support.v7.appcompat.R.id.search_plate);
- searchPlate.setBackgroundResource(R.drawable.keychaintheme_searchview_holo_light);
+ searchView.setOnQueryTextListener(this);
// Erase search result without focus
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- hideMenu = true;
- getActivity().invalidateOptionsMenu();
- }
// disable swipe-to-refresh
// mSwipeRefreshLayout.setIsLocked(true);
@@ -485,26 +438,66 @@ public class KeyListFragment extends LoaderFragment
mQuery = null;
getLoaderManager().restartLoader(0, null, KeyListFragment.this);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- hideMenu = false;
- getActivity().invalidateOptionsMenu();
- }
// enable swipe-to-refresh
// mSwipeRefreshLayout.setIsLocked(false);
return true;
}
});
- if (hideMenu) {
- for (int i = 0; i < menu.size(); i++) {
- menu.getItem(i).setVisible(false);
- }
- }
-
super.onCreateOptionsMenu(menu, inflater);
}
@Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+
+ case R.id.menu_key_list_create:
+ createKey();
+ return true;
+
+ case R.id.menu_key_list_export:
+ mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true);
+ return true;
+
+ case R.id.menu_key_list_debug_cons:
+ consolidate();
+ return true;
+
+ case R.id.menu_key_list_debug_read:
+ try {
+ KeychainDatabase.debugBackup(getActivity(), true);
+ Notify.showNotify(getActivity(), "Restored debug_backup.db", Notify.Style.INFO);
+ getActivity().getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IO Error", e);
+ Notify.showNotify(getActivity(), "IO Error " + e.getMessage(), Notify.Style.ERROR);
+ }
+ return true;
+
+ case R.id.menu_key_list_debug_write:
+ try {
+ KeychainDatabase.debugBackup(getActivity(), false);
+ Notify.showNotify(getActivity(), "Backup to debug_backup.db completed", Notify.Style.INFO);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IO Error", e);
+ Notify.showNotify(getActivity(), "IO Error: " + e.getMessage(), Notify.Style.ERROR);
+ }
+ return true;
+
+ case R.id.menu_key_list_debug_first_time:
+ Preferences prefs = Preferences.getPreferences(getActivity());
+ prefs.setFirstTime(true);
+ Intent intent = new Intent(getActivity(), FirstTimeActivity.class);
+ startActivity(intent);
+ getActivity().finish();
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
public boolean onQueryTextSubmit(String s) {
return true;
}
@@ -526,6 +519,105 @@ public class KeyListFragment extends LoaderFragment
return true;
}
+ private void searchCloud() {
+ Intent importIntent = new Intent(getActivity(), ImportKeysActivity.class);
+ importIntent.putExtra(ImportKeysActivity.EXTRA_QUERY, (String) null); // hack to show only cloud tab
+ startActivity(importIntent);
+ }
+
+ private void scanQrCode() {
+ Intent scanQrCode = new Intent(getActivity(), ImportKeysProxyActivity.class);
+ scanQrCode.setAction(ImportKeysProxyActivity.ACTION_SCAN_IMPORT);
+ startActivityForResult(scanQrCode, 0);
+ }
+
+ private void importFile() {
+ Intent intentImportExisting = new Intent(getActivity(), ImportKeysActivity.class);
+ intentImportExisting.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
+ startActivityForResult(intentImportExisting, 0);
+ }
+
+ private void createKey() {
+ Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
+ startActivityForResult(intent, 0);
+ }
+
+ private void consolidate() {
+ // Message is received after importing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
+ getActivity(),
+ 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();
+ if (returnData == null) {
+ return;
+ }
+ final ConsolidateResult result =
+ returnData.getParcelable(OperationResult.EXTRA_RESULT);
+ if (result == null) {
+ return;
+ }
+
+ result.createNotify(getActivity()).show();
+ }
+ }
+ };
+
+ // Send all information needed to service to import key in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // if a result has been returned, display a notify
+ if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(getActivity()).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ @Override
+ public void fabMoveUp(int height) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mFab, "translationY", 0, -height);
+ // we're a little behind, so skip 1/10 of the time
+ anim.setDuration(270);
+ anim.start();
+ }
+
+ @Override
+ public void fabRestorePosition() {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mFab, "translationY", 0);
+ // we're a little ahead, so wait a few ms
+ anim.setStartDelay(70);
+ anim.setDuration(300);
+ anim.start();
+ }
+
/**
* Implements StickyListHeadersAdapter from library
*/
@@ -533,7 +625,7 @@ public class KeyListFragment extends LoaderFragment
private String mQuery;
private LayoutInflater mInflater;
- private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
+ private HashMap<Integer, Boolean> mSelection = new HashMap<>();
public KeyListAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
@@ -624,13 +716,13 @@ public class KeyListFragment extends LoaderFragment
// Note: order is important!
if (isRevoked) {
- KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_REVOKED, true);
+ KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray);
h.mStatus.setVisibility(View.VISIBLE);
h.mSlinger.setVisibility(View.GONE);
h.mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray));
h.mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray));
} else if (isExpired) {
- KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_EXPIRED, true);
+ KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray);
h.mStatus.setVisibility(View.VISIBLE);
h.mSlinger.setVisibility(View.GONE);
h.mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java
index 4a2b88518..0de7bb391 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java
@@ -19,21 +19,18 @@
package org.sufficientlysecure.keychain.ui;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import android.view.View;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.util.ActionBarHelper;
-public class LogDisplayActivity extends ActionBarActivity {
+public class LogDisplayActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate a "Done" custom action bar
- ActionBarHelper.setOneButtonView(getSupportActionBar(),
- R.string.btn_okay, R.drawable.ic_action_done,
+ setFullScreenDialogClose(
new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -42,7 +39,10 @@ public class LogDisplayActivity extends ActionBarActivity {
}
}
);
+ }
+ @Override
+ protected void initLayout() {
setContentView(R.layout.log_display_activity);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
index 2baebc83d..b655a7e55 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
@@ -153,11 +153,11 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
switch (subEntry.mType.mLevel) {
case DEBUG: ih.mSecondImg.setBackgroundColor(Color.GRAY); break;
case INFO: ih.mSecondImg.setBackgroundColor(Color.BLACK); break;
- case WARN: ih.mSecondImg.setBackgroundColor(Color.YELLOW); break;
- case ERROR: ih.mSecondImg.setBackgroundColor(Color.RED); break;
- case START: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.emphasis)); break;
- case OK: ih.mSecondImg.setBackgroundColor(Color.GREEN); break;
- case CANCELLED: ih.mSecondImg.setBackgroundColor(Color.RED); break;
+ case WARN: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_orange_light)); break;
+ case ERROR: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break;
+ case START: ih.mSecondImg.setBackgroundColor(Color.BLACK); break;
+ case OK: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_green_light)); break;
+ case CANCELLED: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break;
}
} else {
ih.mSecond.setVisibility(View.GONE);
@@ -184,11 +184,11 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
switch (entry.mType.mLevel) {
case DEBUG: ih.mImg.setBackgroundColor(Color.GRAY); break;
case INFO: ih.mImg.setBackgroundColor(Color.BLACK); break;
- case WARN: ih.mImg.setBackgroundColor(Color.YELLOW); break;
- case ERROR: ih.mImg.setBackgroundColor(Color.RED); break;
- case START: ih.mImg.setBackgroundColor(getResources().getColor(R.color.emphasis)); break;
- case OK: ih.mImg.setBackgroundColor(Color.GREEN); break;
- case CANCELLED: ih.mImg.setBackgroundColor(Color.RED); break;
+ case WARN: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_orange_light)); break;
+ case ERROR: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break;
+ case START: ih.mImg.setBackgroundColor(Color.BLACK); break;
+ case OK: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_green_light)); break;
+ case CANCELLED: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break;
}
return convertView;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
new file mode 100644
index 000000000..b6b2fcb8a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * 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.content.Intent;
+import android.os.Bundle;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.remote.ui.AppsListFragment;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.FabContainer;
+
+import it.neokree.materialnavigationdrawer.MaterialNavigationDrawer;
+
+public class MainActivity extends MaterialNavigationDrawer implements FabContainer {
+
+ @Override
+ public void init(Bundle savedInstanceState) {
+ // don't open drawer on first run
+ disableLearningPattern();
+
+// addMultiPaneSupport();
+
+ // set the header image
+ // create and set the header
+ setDrawerHeaderImage(R.drawable.drawer_header);
+
+ // create sections
+ addSection(newSection(getString(R.string.nav_keys), R.drawable.ic_vpn_key_black_24dp, new KeyListFragment()));
+ addSection(newSection(getString(R.string.nav_encrypt_decrypt), R.drawable.ic_lock_black_24dp, new EncryptDecryptOverviewFragment()));
+ addSection(newSection(getString(R.string.title_api_registered_apps), R.drawable.ic_apps_black_24dp, new AppsListFragment()));
+
+ // create bottom section
+ addBottomSection(newSection(getString(R.string.menu_preferences), R.drawable.ic_settings_black_24dp, new Intent(this, SettingsActivity.class)));
+ addBottomSection(newSection(getString(R.string.menu_help), R.drawable.ic_help_black_24dp, new Intent(this, HelpActivity.class)));
+
+ // if this is the first time show first time activity
+ Preferences prefs = Preferences.getPreferences(this);
+ if (prefs.isFirstTime()) {
+ startActivity(new Intent(this, FirstTimeActivity.class));
+ finish();
+ return;
+ }
+
+ Intent data = getIntent();
+ // If we got an EXTRA_RESULT in the intent, show the notification
+ if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(this).show();
+ }
+ }
+
+ @Override
+ public void fabMoveUp(int height) {
+ Object fragment = getCurrentSection().getTargetFragment();
+ if (fragment instanceof FabContainer) {
+ ((FabContainer) fragment).fabMoveUp(height);
+ }
+ }
+
+ @Override
+ public void fabRestorePosition() {
+ Object fragment = getCurrentSection().getTargetFragment();
+ if (fragment instanceof FabContainer) {
+ ((FabContainer) fragment).fabRestorePosition();
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java
index d0e40a9b8..7311f4879 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcActivity.java
@@ -15,7 +15,6 @@ import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Build;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import android.view.WindowManager;
import android.widget.Toast;
@@ -23,13 +22,11 @@ import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Iso7816TLV;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.Locale;
/**
* This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant
@@ -38,7 +35,7 @@ import java.util.Locale;
* For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf
*/
@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1)
-public class NfcActivity extends ActionBarActivity {
+public class NfcActivity extends BaseActivity {
// actions
public static final String ACTION_SIGN_HASH = "sign_hash";
@@ -82,8 +79,6 @@ public class NfcActivity extends ActionBarActivity {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- setContentView(R.layout.nfc_activity);
-
Intent intent = getIntent();
Bundle data = intent.getExtras();
String action = intent.getAction();
@@ -93,36 +88,46 @@ public class NfcActivity extends ActionBarActivity {
mKeyId = data.getLong(EXTRA_KEY_ID);
}
- if (ACTION_SIGN_HASH.equals(action)) {
- mAction = action;
- mPin = data.getString(EXTRA_PIN);
- mHashToSign = data.getByteArray(EXTRA_NFC_HASH_TO_SIGN);
- mHashAlgo = data.getInt(EXTRA_NFC_HASH_ALGO);
- mServiceIntent = data.getParcelable(EXTRA_DATA);
-
- Log.d(Constants.TAG, "NfcActivity mAction: " + mAction);
- Log.d(Constants.TAG, "NfcActivity mPin: " + mPin);
- Log.d(Constants.TAG, "NfcActivity mHashToSign as hex: " + getHex(mHashToSign));
- Log.d(Constants.TAG, "NfcActivity mServiceIntent: " + mServiceIntent.toString());
- } else if (ACTION_DECRYPT_SESSION_KEY.equals(action)) {
- mAction = action;
- mPin = data.getString(EXTRA_PIN);
- mEncryptedSessionKey = data.getByteArray(EXTRA_NFC_ENC_SESSION_KEY);
- mServiceIntent = data.getParcelable(EXTRA_DATA);
-
- Log.d(Constants.TAG, "NfcActivity mAction: " + mAction);
- Log.d(Constants.TAG, "NfcActivity mPin: " + mPin);
- Log.d(Constants.TAG, "NfcActivity mEncryptedSessionKey as hex: " + getHex(mEncryptedSessionKey));
- Log.d(Constants.TAG, "NfcActivity mServiceIntent: " + mServiceIntent.toString());
- } else if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
- Log.e(Constants.TAG, "This should not happen! NfcActivity.onCreate() is being called instead of onNewIntent()!");
- toast("This should not happen! Please create a new bug report that the NFC screen is restarted!");
- finish();
- } else {
- Log.d(Constants.TAG, "Action not supported: " + action);
+ switch (action) {
+ case ACTION_SIGN_HASH:
+ mAction = action;
+ mPin = data.getString(EXTRA_PIN);
+ mHashToSign = data.getByteArray(EXTRA_NFC_HASH_TO_SIGN);
+ mHashAlgo = data.getInt(EXTRA_NFC_HASH_ALGO);
+ mServiceIntent = data.getParcelable(EXTRA_DATA);
+
+ Log.d(Constants.TAG, "NfcActivity mAction: " + mAction);
+ Log.d(Constants.TAG, "NfcActivity mPin: " + mPin);
+ Log.d(Constants.TAG, "NfcActivity mHashToSign as hex: " + getHex(mHashToSign));
+ Log.d(Constants.TAG, "NfcActivity mServiceIntent: " + mServiceIntent.toString());
+ break;
+ case ACTION_DECRYPT_SESSION_KEY:
+ mAction = action;
+ mPin = data.getString(EXTRA_PIN);
+ mEncryptedSessionKey = data.getByteArray(EXTRA_NFC_ENC_SESSION_KEY);
+ mServiceIntent = data.getParcelable(EXTRA_DATA);
+
+ Log.d(Constants.TAG, "NfcActivity mAction: " + mAction);
+ Log.d(Constants.TAG, "NfcActivity mPin: " + mPin);
+ Log.d(Constants.TAG, "NfcActivity mEncryptedSessionKey as hex: " + getHex(mEncryptedSessionKey));
+ Log.d(Constants.TAG, "NfcActivity mServiceIntent: " + mServiceIntent.toString());
+ break;
+ case NfcAdapter.ACTION_TAG_DISCOVERED:
+ Log.e(Constants.TAG, "This should not happen! NfcActivity.onCreate() is being called instead of onNewIntent()!");
+ toast("This should not happen! Please create a new bug report that the NFC screen is restarted!");
+ finish();
+ break;
+ default:
+ Log.d(Constants.TAG, "Action not supported: " + action);
+ break;
}
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.nfc_activity);
+ }
+
/**
* Called when the system is about to start resuming a previous activity,
* disables NFC Foreground Dispatch
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java
index cb15dbec2..0ccb206d1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcIntentActivity.java
@@ -15,15 +15,14 @@ import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Build;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import android.view.WindowManager;
import android.widget.Toast;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Iso7816TLV;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Iso7816TLV;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
@@ -36,7 +35,7 @@ import java.nio.ByteBuffer;
* For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf
*/
@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1)
-public class NfcIntentActivity extends ActionBarActivity {
+public class NfcIntentActivity extends BaseActivity {
// special extra for OpenPgpService
public static final String EXTRA_DATA = "data";
@@ -54,8 +53,6 @@ public class NfcIntentActivity extends ActionBarActivity {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- setContentView(R.layout.nfc_activity);
-
Intent intent = getIntent();
Bundle data = intent.getExtras();
String action = intent.getAction();
@@ -87,7 +84,11 @@ public class NfcIntentActivity extends ActionBarActivity {
Log.e(Constants.TAG, "IOException!", e);
finish();
}
+ }
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.nfc_activity);
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
index 5bface1fb..53fd9cdfd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
@@ -41,8 +41,6 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
-import com.haibison.android.lockpattern.LockPatternActivity;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
@@ -108,20 +106,20 @@ public class PassphraseDialogActivity extends FragmentActivity {
case RESULT_CANCELED:
// The user cancelled the task
break;
- case LockPatternActivity.RESULT_FAILED:
- // The user failed to enter the pattern
- break;
- case LockPatternActivity.RESULT_FORGOT_PATTERN:
- // The user forgot the pattern and invoked your recovery Activity.
- break;
+// case LockPatternActivity.RESULT_FAILED:
+// // The user failed to enter the pattern
+// break;
+// case LockPatternActivity.RESULT_FORGOT_PATTERN:
+// // The user forgot the pattern and invoked your recovery Activity.
+// break;
}
/*
* In any case, there's always a key EXTRA_RETRY_COUNT, which holds
* the number of tries that the user did.
*/
- int retryCount = data.getIntExtra(
- LockPatternActivity.EXTRA_RETRY_COUNT, 0);
+// int retryCount = data.getIntExtra(
+// LockPatternActivity.EXTRA_RETRY_COUNT, 0);
break;
}
@@ -256,9 +254,9 @@ public class PassphraseDialogActivity extends FragmentActivity {
if (keyType == CanonicalizedSecretKey.SecretKeyType.PATTERN) {
// start pattern dialog and show progress circle here...
- Intent patternActivity = new Intent(getActivity(), LockPatternActivity.class);
- patternActivity.putExtra(LockPatternActivity.EXTRA_PATTERN, "123");
- startActivityForResult(patternActivity, REQUEST_CODE_ENTER_PATTERN);
+// Intent patternActivity = new Intent(getActivity(), LockPatternActivity.class);
+// patternActivity.putExtra(LockPatternActivity.EXTRA_PATTERN, "123");
+// startActivityForResult(patternActivity, REQUEST_CODE_ENTER_PATTERN);
mInput.setVisibility(View.GONE);
mProgress.setVisibility(View.VISIBLE);
} else {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java
index 93778fd0c..2e838535d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java
@@ -43,10 +43,6 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
-import com.haibison.android.lockpattern.LockPatternFragment;
-import com.haibison.android.lockpattern.LockPatternFragmentOld;
-import com.haibison.android.lockpattern.widget.LockPatternView;
-
import org.sufficientlysecure.keychain.R;
import java.io.IOException;
@@ -56,7 +52,8 @@ import java.util.Arrays;
import java.util.List;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
-public class PassphraseWizardActivity extends FragmentActivity implements LockPatternView.OnPatternListener {
+public class PassphraseWizardActivity extends FragmentActivity {
+//public class PassphraseWizardActivity extends FragmentActivity implements LockPatternView.OnPatternListener {
//create or authenticate
public String selectedAction;
//for lockpattern
@@ -117,10 +114,10 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa
getActionBar().setTitle(R.string.draw_lockpattern);
}
// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction);
- LockPatternFragment lpf = LockPatternFragment.newInstance("asd");
+// LockPatternFragment lpf = LockPatternFragment.newInstance("asd");
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
+// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
}
public void cancel(View view) {
@@ -205,13 +202,11 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa
writeNFC = false; //just write once
Toast.makeText(this, R.string.nfc_write_succesful, Toast.LENGTH_SHORT).show();
//advance to lockpattern
- LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction);
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
+// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction);
+// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
}
- } catch (IOException e) {
- e.printStackTrace();
- } catch (FormatException e) {
+ } catch (IOException | FormatException e) {
e.printStackTrace();
}
@@ -226,9 +221,9 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa
//passwort matches, go to next view
Toast.makeText(this, R.string.passphrases_match + "!", Toast.LENGTH_SHORT).show();
- LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction);
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
+// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction);
+// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
readNFC = false; //just once
} else {
//passwort doesnt match
@@ -236,9 +231,7 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa
nfc.setText(R.string.nfc_wrong_tag);
}
}
- } catch (IOException e) {
- e.printStackTrace();
- } catch (FormatException e) {
+ } catch (IOException | FormatException e) {
e.printStackTrace();
}
}
@@ -356,26 +349,6 @@ public class PassphraseWizardActivity extends FragmentActivity implements LockPa
adapter.disableForegroundDispatch(this);
}
- @Override
- public void onPatternStart() {
-
- }
-
- @Override
- public void onPatternCleared() {
-
- }
-
- @Override
- public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
-
- }
-
- @Override
- public void onPatternDetected(List<LockPatternView.Cell> pattern) {
-
- }
-
public static class SelectMethods extends Fragment {
// private OnFragmentInteractionListener mListener;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java
index cf0c3eb88..d3c1d971a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java
@@ -20,57 +20,56 @@ package org.sufficientlysecure.keychain.ui;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.widget.CardView;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.util.ActionBarHelper;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
+import org.sufficientlysecure.keychain.util.Log;
-public class QrCodeViewActivity extends ActionBarActivity {
+public class QrCodeViewActivity extends BaseActivity {
- private ImageView mFingerprintQrCode;
+ private ImageView mQrCode;
+ private CardView mQrCodeLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate a "Done" custom action bar
- ActionBarHelper.setOneButtonView(getSupportActionBar(),
- R.string.btn_okay, R.drawable.ic_action_done,
+ setFullScreenDialogClose(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// "Done"
- finish();
+ ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
}
}
);
- setContentView(R.layout.qr_code_activity);
-
Uri dataUri = getIntent().getData();
if (dataUri == null) {
Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
- finish();
+ ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
return;
}
- mFingerprintQrCode = (ImageView) findViewById(R.id.qr_code_image);
+ mQrCode = (ImageView) findViewById(R.id.qr_code_image);
+ mQrCodeLayout = (CardView) findViewById(R.id.qr_code_image_layout);
- mFingerprintQrCode.setOnClickListener(new View.OnClickListener() {
+ mQrCodeLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- finish();
+ ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
}
});
@@ -82,7 +81,7 @@ public class QrCodeViewActivity extends ActionBarActivity {
if (blob == null) {
Log.e(Constants.TAG, "key not found!");
Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
- finish();
+ ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
}
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
@@ -91,37 +90,26 @@ public class QrCodeViewActivity extends ActionBarActivity {
// create a minimal size qr code, we can keep this in ram no problem
final Bitmap qrCode = QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0);
- mFingerprintQrCode.getViewTreeObserver().addOnGlobalLayoutListener(
+ mQrCode.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- // create actual bitmap in display dimensions
- Bitmap scaled = Bitmap.createScaledBitmap(qrCode,
- mFingerprintQrCode.getWidth(), mFingerprintQrCode.getWidth(), false);
- mFingerprintQrCode.setImageBitmap(scaled);
- }
- });
+ @Override
+ public void onGlobalLayout() {
+ // create actual bitmap in display dimensions
+ Bitmap scaled = Bitmap.createScaledBitmap(qrCode,
+ mQrCode.getWidth(), mQrCode.getWidth(), false);
+ mQrCode.setImageBitmap(scaled);
+ }
+ });
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
- finish();
+ ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
}
}
@Override
- protected void onResume() {
- super.onResume();
-
- // custom activity transition to get zoom in effect
- this.overridePendingTransition(R.anim.qr_code_zoom_enter, android.R.anim.fade_out);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- // custom activity transition to get zoom out effect
- this.overridePendingTransition(0, R.anim.qr_code_zoom_exit);
+ protected void initLayout() {
+ setContentView(R.layout.qr_code_activity);
}
} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
index 20e1bbe97..d1df2906d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
@@ -27,13 +27,9 @@ import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.FragmentActivity;
-import android.support.v7.app.ActionBarActivity;
import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.NumberPicker;
-import android.widget.Spinner;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -50,13 +46,12 @@ import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.List;
import edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
import edu.cmu.cylab.starslinger.exchange.ExchangeConfig;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
-public class SafeSlingerActivity extends ActionBarActivity {
+public class SafeSlingerActivity extends BaseActivity {
private static final int REQUEST_CODE_SAFE_SLINGER = 211;
@@ -69,51 +64,17 @@ public class SafeSlingerActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.safe_slinger_activity);
-
mMasterKeyId = getIntent().getLongExtra(EXTRA_MASTER_KEY_ID, 0);
- // NOTE: there are two versions of this layout, for API >= 11 and one for < 11
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- NumberPicker picker = (NumberPicker) findViewById(R.id.safe_slinger_picker);
- picker.setMinValue(2);
- picker.setMaxValue(10);
- picker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
- @Override
- public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
- mSelectedNumber = newVal;
- }
- });
- } else {
- Spinner spinner = (Spinner) findViewById(R.id.safe_slinger_spinner);
-
- List<String> list = new ArrayList<String>();
- list.add("2");
- list.add("3");
- list.add("4");
- list.add("5");
- list.add("6");
- list.add("7");
- list.add("8");
- list.add("9");
- list.add("10");
-
- ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
- android.R.layout.simple_spinner_item, list);
- dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spinner.setAdapter(dataAdapter);
- spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mSelectedNumber = position + 2;
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
-
- }
- });
- }
+ NumberPicker picker = (NumberPicker) findViewById(R.id.safe_slinger_picker);
+ picker.setMinValue(2);
+ picker.setMaxValue(10);
+ picker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+ @Override
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+ mSelectedNumber = newVal;
+ }
+ });
ImageView buttonIcon = (ImageView) findViewById(R.id.safe_slinger_button_image);
buttonIcon.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
@@ -128,6 +89,11 @@ public class SafeSlingerActivity extends ActionBarActivity {
});
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.safe_slinger_activity);
+ }
+
private void startExchange(long masterKeyId, int number) {
// retrieve public key blob and start SafeSlinger
Uri uri = KeychainContract.KeyRingData.buildPublicKeyRingUri(masterKeyId);
@@ -197,7 +163,7 @@ public class SafeSlingerActivity extends ActionBarActivity {
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId);
- startActivityForResult(certifyIntent, KeyListActivity.REQUEST_CODE_RESULT_TO_LIST);
+ startActivityForResult(certifyIntent, 0);
// mExchangeMasterKeyId = null;
}
@@ -221,7 +187,7 @@ public class SafeSlingerActivity extends ActionBarActivity {
// We parcel this iteratively into a file - anything we can
// display here, we should be able to import.
ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<ParcelableKeyRing>(activity, "key_import.pcl");
+ new ParcelableFileCache<>(activity, "key_import.pcl");
cache.writeCache(it.size(), it.iterator());
// fill values for this action
@@ -249,7 +215,7 @@ public class SafeSlingerActivity extends ActionBarActivity {
}
private static ArrayList<ParcelableKeyRing> getSlingedKeys(Bundle extras) {
- ArrayList<ParcelableKeyRing> list = new ArrayList<ParcelableKeyRing>();
+ ArrayList<ParcelableKeyRing> list = new ArrayList<>();
if (extras != null) {
byte[] d;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java
deleted file mode 100644
index 148aa7d67..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * 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.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.ui.util.ActionBarHelper;
-
-public class SelectPublicKeyActivity extends ActionBarActivity {
-
- // Actions for internal use only:
- public static final String ACTION_SELECT_PUBLIC_KEYS = Constants.INTENT_PREFIX
- + "SELECT_PUBLIC_KEYRINGS";
-
- public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
-
- public static final String RESULT_EXTRA_MASTER_KEY_IDS = "master_key_ids";
- public static final String RESULT_EXTRA_USER_IDS = "user_ids";
-
- SelectPublicKeyFragment mSelectFragment;
-
- long mSelectedMasterKeyIds[];
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // Inflate a "Done"/"Cancel" custom action bar view
- 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, R.drawable.ic_action_cancel, new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // cancel
- cancelClicked();
- }
- }
- );
-
- setContentView(R.layout.select_public_key_activity);
-
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
-
- handleIntent(getIntent());
-
- // Check that the activity is using the layout version with
- // the fragment_container FrameLayout
- if (findViewById(R.id.select_public_key_fragment_container) != null) {
-
- // 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
- mSelectFragment = SelectPublicKeyFragment.newInstance(mSelectedMasterKeyIds);
-
- // Add the fragment to the 'fragment_container' FrameLayout
- getSupportFragmentManager().beginTransaction()
- .add(R.id.select_public_key_fragment_container, mSelectFragment).commit();
- }
-
- // 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());
- // }
- // });
-
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- // TODO: reimplement search!
-
- // 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));
- // }
-
- // preselected master keys
- mSelectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
- }
-
- private void cancelClicked() {
- setResult(RESULT_CANCELED, null);
- finish();
- }
-
- private void okClicked() {
- Intent data = new Intent();
- data.putExtra(RESULT_EXTRA_MASTER_KEY_IDS, mSelectFragment.getSelectedMasterKeyIds());
- data.putExtra(RESULT_EXTRA_USER_IDS, mSelectFragment.getSelectedUserIds());
- setResult(RESULT_OK, data);
- finish();
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
index af583bf89..afec3bf06 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
@@ -42,7 +42,6 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
@@ -216,7 +215,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
public long[] getSelectedMasterKeyIds() {
// mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key
// ids!
- Vector<Long> vector = new Vector<Long>();
+ Vector<Long> vector = new Vector<>();
for (int i = 0; i < getListView().getCount(); ++i) {
if (getListView().isItemChecked(i)) {
vector.add(mAdapter.getMasterKeyId(i));
@@ -238,7 +237,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
* @return
*/
public String[] getSelectedUserIds() {
- Vector<String> userIds = new Vector<String>();
+ Vector<String> userIds = new Vector<>();
for (int i = 0; i < getListView().getCount(); ++i) {
if (getListView().isItemChecked(i)) {
userIds.add(mAdapter.getUserId(i));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
index 51fac4779..53986a392 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
@@ -17,7 +17,7 @@
package org.sufficientlysecure.keychain.ui;
-import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
@@ -27,19 +27,22 @@ import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
+import org.sufficientlysecure.keychain.util.Preferences;
import java.util.List;
-@SuppressLint("NewApi")
-public class PreferencesActivity extends PreferenceActivity {
+public class SettingsActivity extends PreferenceActivity {
public static final String ACTION_PREFS_CLOUD = "org.sufficientlysecure.keychain.ui.PREFS_CLOUD";
public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
@@ -54,6 +57,8 @@ public class PreferencesActivity extends PreferenceActivity {
sPreferences = Preferences.getPreferences(this);
super.onCreate(savedInstanceState);
+ setupToolbar();
+
String action = getIntent().getAction();
if (action != null && action.equals(ACTION_PREFS_CLOUD)) {
@@ -64,9 +69,9 @@ public class PreferencesActivity extends PreferenceActivity {
mKeyServerPreference
.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
- Intent intent = new Intent(PreferencesActivity.this,
- PreferencesKeyServerActivity.class);
- intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS,
+ Intent intent = new Intent(SettingsActivity.this,
+ SettingsKeyServerActivity.class);
+ intent.putExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS,
sPreferences.getKeyServers());
startActivityForResult(intent, REQUEST_CODE_KEYSERVER_PREF);
return false;
@@ -130,12 +135,33 @@ public class PreferencesActivity extends PreferenceActivity {
initializeUseNumKeypadForYubikeyPin(
(CheckBoxPreference) findPreference(Constants.Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN));
- } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
- // Load the legacy preferences headers
- addPreferencesFromResource(R.xml.preference_headers_legacy);
}
}
+ /**
+ * Hack to get Toolbar in PreferenceActivity. See http://stackoverflow.com/a/26614696
+ */
+ private void setupToolbar() {
+ ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
+ LinearLayout content = (LinearLayout) root.getChildAt(0);
+ LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.preference_toolbar_activity, null);
+
+ root.removeAllViews();
+ toolbarContainer.addView(content);
+ root.addView(toolbarContainer);
+
+ Toolbar toolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar);
+ toolbar.setTitle(R.string.title_preferences);
+ toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp));
+ toolbar.setNavigationOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ //What to do on back clicked
+ finish();
+ }
+ });
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
@@ -144,7 +170,7 @@ public class PreferencesActivity extends PreferenceActivity {
return;
}
String servers[] = data
- .getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS);
+ .getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS);
sPreferences.setKeyServers(servers);
mKeyServerPreference.setSummary(keyserverSummary(this));
break;
@@ -157,7 +183,6 @@ public class PreferencesActivity extends PreferenceActivity {
}
}
- /* Called only on Honeycomb and later */
@Override
public void onBuildHeaders(List<Header> target) {
super.onBuildHeaders(target);
@@ -185,8 +210,8 @@ public class PreferencesActivity extends PreferenceActivity {
.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(getActivity(),
- PreferencesKeyServerActivity.class);
- intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS,
+ SettingsKeyServerActivity.class);
+ intent.putExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS,
sPreferences.getKeyServers());
startActivityForResult(intent, REQUEST_CODE_KEYSERVER_PREF);
return false;
@@ -208,7 +233,7 @@ public class PreferencesActivity extends PreferenceActivity {
return;
}
String servers[] = data
- .getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS);
+ .getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS);
sPreferences.setKeyServers(servers);
mKeyServerPreference.setSummary(keyserverSummary(getActivity()));
break;
@@ -287,6 +312,7 @@ public class PreferencesActivity extends PreferenceActivity {
}
}
+ @TargetApi(Build.VERSION_CODES.KITKAT)
protected boolean isValidFragment(String fragmentName) {
return AdvancedPrefsFragment.class.getName().equals(fragmentName)
|| CloudSearchPrefsFragment.class.getName().equals(fragmentName)
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java
index 2a8ef171c..080dc2495 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java
@@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -28,14 +27,13 @@ import android.view.ViewGroup;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.util.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,
+public class SettingsKeyServerActivity extends BaseActivity implements OnClickListener,
EditorListener {
public static final String EXTRA_KEY_SERVERS = "key_servers";
@@ -52,23 +50,19 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
super.onCreate(savedInstanceState);
// Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
+ setFullScreenDialogDoneClose(R.string.btn_save,
new View.OnClickListener() {
@Override
public void onClick(View v) {
- // ok
okClicked();
}
- }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
+ },
+ new View.OnClickListener() {
@Override
public void onClick(View v) {
- // cancel
cancelClicked();
}
- }
- );
-
- setContentView(R.layout.key_server_preference);
+ });
mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -102,6 +96,11 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
makeServerList(servers);
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.key_server_preference);
+ }
+
private void makeServerList(String[] servers) {
if (servers != null) {
mEditors.removeAllViews();
@@ -137,7 +136,7 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
}
private Vector<String> serverList() {
- Vector<String> servers = new Vector<String>();
+ Vector<String> servers = new Vector<>();
for (int i = 0; i < mEditors.getChildCount(); ++i) {
KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
String tmp = editor.getValue();
@@ -150,7 +149,7 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
private void okClicked() {
Intent data = new Intent();
- Vector<String> servers = new Vector<String>();
+ Vector<String> servers = new Vector<>();
for (int i = 0; i < mEditors.getChildCount(); ++i) {
KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
String tmp = editor.getValue();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index 497486a5e..e19793fd5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -24,7 +24,6 @@ import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.NavUtils;
-import android.support.v7.app.ActionBarActivity;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
@@ -34,17 +33,17 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
/**
* Sends the selected public key to a keyserver
*/
-public class UploadKeyActivity extends ActionBarActivity {
+public class UploadKeyActivity extends BaseActivity {
private View mUploadButton;
private Spinner mKeyServerSpinner;
@@ -54,12 +53,10 @@ public class UploadKeyActivity extends ActionBarActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.upload_key_activity);
-
mUploadButton = findViewById(R.id.upload_key_action_upload);
mKeyServerSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
.getKeyServers()
);
@@ -86,6 +83,11 @@ public class UploadKeyActivity extends ActionBarActivity {
}
}
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.upload_key_activity);
+ }
+
private void uploadKey() {
// Send all information needed to service to upload key in other thread
Intent intent = new Intent(this, KeychainIntentService.class);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index 34c08a6c7..a80503591 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -27,9 +27,6 @@ import android.support.v4.app.NavUtils;
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.text.SpannableString;
-import android.text.SpannableStringBuilder;
import android.text.format.DateFormat;
import android.view.MenuItem;
import android.view.View;
@@ -37,19 +34,18 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
-public class ViewCertActivity extends ActionBarActivity
+public class ViewCertActivity extends BaseActivity
implements LoaderManager.LoaderCallbacks<Cursor> {
// These are the rows that we will retrieve.
@@ -86,8 +82,6 @@ public class ViewCertActivity extends ActionBarActivity
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
- setContentView(R.layout.view_cert_activity);
-
mSigneeKey = (TextView) findViewById(R.id.signee_key);
mSigneeUid = (TextView) findViewById(R.id.signee_uid);
mAlgorithm = (TextView) findViewById(R.id.algorithm);
@@ -113,6 +107,11 @@ public class ViewCertActivity extends ActionBarActivity
}
@Override
+ protected void initLayout() {
+ setContentView(R.layout.view_cert_activity);
+ }
+
+ @Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 0bc75b3a9..742cde75c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -18,10 +18,15 @@
package org.sufficientlysecure.keychain.ui;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.ActivityOptions;
import android.content.Intent;
import android.database.Cursor;
-import android.graphics.PorterDuff;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
@@ -32,62 +37,77 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.Messenger;
import android.provider.ContactsContract;
+import android.provider.Settings;
+import android.support.v4.app.ActivityCompat;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.widget.CardView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageButton;
import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
+import com.getbase.floatingactionbutton.FloatingActionButton;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.ContactHelper;
-import org.sufficientlysecure.keychain.util.ExportHelper;
-import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.results.CertifyResult;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
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.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
-import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
-import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout.TabColorizer;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
+import org.sufficientlysecure.keychain.ui.widget.AspectRatioImageView;
+import org.sufficientlysecure.keychain.util.ContactHelper;
+import org.sufficientlysecure.keychain.util.ExportHelper;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
+import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
-public class ViewKeyActivity extends ActionBarActivity implements
+public class ViewKeyActivity extends BaseActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
+ static final int REQUEST_QR_FINGERPRINT = 1;
+
ExportHelper mExportHelper;
ProviderHelper mProviderHelper;
protected Uri mDataUri;
- public static final String EXTRA_SELECTED_TAB = "selected_tab";
- public static final int TAB_MAIN = 0;
- public static final int TAB_SHARE = 1;
-
- // view
- private ViewPager mViewPager;
- private SlidingTabLayout mSlidingTabLayout;
- private PagerTabStripAdapter mTabsAdapter;
-
- private LinearLayout mStatusLayout;
+ private TextView mName;
private TextView mStatusText;
private ImageView mStatusImage;
- private View mStatusDivider;
+ private RelativeLayout mBigToolbar;
+
+ private ImageButton mActionEncryptFile;
+ private ImageButton mActionEncryptText;
+ private ImageButton mActionNfc;
+ private FloatingActionButton mFab;
+ private AspectRatioImageView mPhoto;
+ private ImageView mQrCode;
+ private CardView mQrCodeLayout;
// NFC
private NfcAdapter mNfcAdapter;
@@ -98,6 +118,17 @@ public class ViewKeyActivity extends ActionBarActivity implements
private static final int LOADER_ID_UNIFIED = 0;
+ private boolean mIsSecret = false;
+ private boolean mHasEncrypt = false;
+ private boolean mIsVerified = false;
+ private MenuItem mRefreshItem;
+ private boolean mIsRefreshing;
+ private Animation mRotate, mRotateSpin;
+ private View mRefresh;
+ private String mFingerprint;
+ private long mMasterKeyId;
+
+ @SuppressLint("InflateParams")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -105,39 +136,65 @@ public class ViewKeyActivity extends ActionBarActivity implements
mExportHelper = new ExportHelper(this);
mProviderHelper = new ProviderHelper(this);
- // let the actionbar look like Android's contact app
- ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setIcon(android.R.color.transparent);
- actionBar.setHomeButtonEnabled(true);
+ setTitle(null);
- setContentView(R.layout.view_key_activity);
-
- mStatusLayout = (LinearLayout) findViewById(R.id.view_key_status_layout);
- mStatusText = (TextView) findViewById(R.id.view_key_status_text);
+ mName = (TextView) findViewById(R.id.view_key_name);
+ mStatusText = (TextView) findViewById(R.id.view_key_status);
mStatusImage = (ImageView) findViewById(R.id.view_key_status_image);
- mStatusDivider = findViewById(R.id.view_key_status_divider);
+ mBigToolbar = (RelativeLayout) findViewById(R.id.toolbar_big);
+
+ mActionEncryptFile = (ImageButton) findViewById(R.id.view_key_action_encrypt_files);
+ mActionEncryptText = (ImageButton) findViewById(R.id.view_key_action_encrypt_text);
+ mActionNfc = (ImageButton) findViewById(R.id.view_key_action_nfc);
+ mFab = (FloatingActionButton) findViewById(R.id.fab);
+ mPhoto = (AspectRatioImageView) findViewById(R.id.view_key_photo);
+ mQrCode = (ImageView) findViewById(R.id.view_key_qr_code);
+ mQrCodeLayout = (CardView) findViewById(R.id.view_key_qr_code_layout);
+
+ mRotateSpin = AnimationUtils.loadAnimation(this, R.anim.rotate_spin);
+ mRotateSpin.setAnimationListener(new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
- mViewPager = (ViewPager) findViewById(R.id.view_key_pager);
- mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.view_key_sliding_tab_layout);
+ }
- mSlidingTabLayout.setCustomTabColorizer(new TabColorizer() {
@Override
- public int getIndicatorColor(int position) {
- return 0xFFAA66CC;
+ public void onAnimationEnd(Animation animation) {
+ mRefreshItem.getActionView().clearAnimation();
+ mRefreshItem.setActionView(null);
+ mRefreshItem.setEnabled(true);
+
+ // this is a deferred call
+ supportInvalidateOptionsMenu();
}
@Override
- public int getDividerColor(int position) {
- return 0;
+ public void onAnimationRepeat(Animation animation) {
+
}
});
+ mRotate = AnimationUtils.loadAnimation(this, R.anim.rotate);
+ mRotate.setRepeatCount(Animation.INFINITE);
+ mRotate.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
- int switchToTab = TAB_MAIN;
- Intent intent = getIntent();
- if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) {
- switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
- }
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ if (!mIsRefreshing) {
+ mRefreshItem.getActionView().clearAnimation();
+ mRefreshItem.getActionView().startAnimation(mRotateSpin);
+ }
+ }
+ });
+ mRefresh = getLayoutInflater().inflate(R.layout.indeterminate_progress, null);
mDataUri = getIntent().getData();
if (mDataUri == null) {
@@ -155,7 +212,45 @@ public class ViewKeyActivity extends ActionBarActivity implements
}
}
- Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri);
+
+ mActionEncryptFile.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encrypt(mDataUri, false);
+ }
+ });
+ mActionEncryptText.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encrypt(mDataUri, true);
+ }
+ });
+
+ mFab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mIsSecret) {
+ startSafeSlinger(mDataUri);
+ } else {
+ scanQrCode();
+ }
+ }
+ });
+
+ mQrCodeLayout.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showQrCodeDialog();
+ }
+ });
+
+ mActionNfc.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ invokeNfcBeam();
+ }
+ });
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
@@ -163,35 +258,39 @@ public class ViewKeyActivity extends ActionBarActivity implements
initNfc(mDataUri);
- initTabs(mDataUri);
-
- // switch to tab selected by extra
- mViewPager.setCurrentItem(switchToTab);
+ startFragment(savedInstanceState, mDataUri);
}
- private void initTabs(Uri dataUri) {
- mTabsAdapter = new PagerTabStripAdapter(this);
- mViewPager.setAdapter(mTabsAdapter);
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.view_key_activity);
+ }
- Bundle mainBundle = new Bundle();
- mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyMainFragment.class,
- mainBundle, getString(R.string.key_view_tab_main));
+ private void startFragment(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;
+ }
- Bundle shareBundle = new Bundle();
- shareBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyShareFragment.class,
- shareBundle, getString(R.string.key_view_tab_share));
+ // Create an instance of the fragment
+ ViewKeyFragment frag = ViewKeyFragment.newInstance(dataUri);
- // update layout after operations
- mSlidingTabLayout.setViewPager(mViewPager);
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.view_key_fragment, frag)
+ .commitAllowingStateLoss();
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.key_view, menu);
-
+ mRefreshItem = menu.findItem(R.id.menu_key_view_refresh);
return true;
}
@@ -200,7 +299,7 @@ public class ViewKeyActivity extends ActionBarActivity implements
try {
switch (item.getItemId()) {
case android.R.id.home: {
- Intent homeIntent = new Intent(this, KeyListActivity.class);
+ Intent homeIntent = new Intent(this, MainActivity.class);
homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(homeIntent);
return true;
@@ -214,9 +313,26 @@ public class ViewKeyActivity extends ActionBarActivity implements
return true;
}
case R.id.menu_key_view_advanced: {
- Intent advancedIntent = new Intent(this, ViewKeyAdvancedActivity.class);
+ Intent advancedIntent = new Intent(this, ViewKeyAdvActivity.class);
advancedIntent.setData(mDataUri);
startActivity(advancedIntent);
+ return true;
+ }
+ case R.id.menu_key_view_refresh: {
+ try {
+ updateFromKeyserver(mDataUri, mProviderHelper);
+ } catch (ProviderHelper.NotFoundException e) {
+ Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
+ }
+ return true;
+ }
+ case R.id.menu_key_view_edit: {
+ editKey(mDataUri);
+ return true;
+ }
+ case R.id.menu_key_view_certify_fingerprint: {
+ certifyFingeprint(mDataUri);
+ return true;
}
}
} catch (ProviderHelper.NotFoundException e) {
@@ -226,6 +342,105 @@ public class ViewKeyActivity extends ActionBarActivity implements
return super.onOptionsItemSelected(item);
}
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ MenuItem editKey = menu.findItem(R.id.menu_key_view_edit);
+ editKey.setVisible(mIsSecret);
+ MenuItem certifyFingerprint = menu.findItem(R.id.menu_key_view_certify_fingerprint);
+ certifyFingerprint.setVisible(!mIsSecret && !mIsVerified);
+
+ return true;
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private void invokeNfcBeam() {
+ // Check for available NFC Adapter
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) {
+ Notify.createNotify(this, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() {
+ @Override
+ public void onAction() {
+ Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS);
+ startActivity(intentSettings);
+ }
+ }, R.string.menu_nfc_preferences).show();
+
+ return;
+ }
+
+ if (!mNfcAdapter.isNdefPushEnabled()) {
+ Notify.createNotify(this, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() {
+ @Override
+ public void onAction() {
+ Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS);
+ startActivity(intentSettings);
+ }
+ }, R.string.menu_beam_preferences).show();
+
+ return;
+ }
+
+ mNfcAdapter.invokeBeam(this);
+ }
+
+ private void scanQrCode() {
+ Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class);
+ scanQrCode.setAction(ImportKeysProxyActivity.ACTION_SCAN_WITH_RESULT);
+ startActivityForResult(scanQrCode, REQUEST_QR_FINGERPRINT);
+ }
+
+ private void certifyFingeprint(Uri dataUri) {
+ Intent intent = new Intent(this, CertifyFingerprintActivity.class);
+ intent.setData(dataUri);
+
+ startCertifyIntent(intent);
+ }
+
+ private void certifyImmediate() {
+ Intent intent = new Intent(this, CertifyKeyActivity.class);
+ intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{ mMasterKeyId });
+
+ startCertifyIntent(intent);
+ }
+
+ private void startCertifyIntent (Intent intent) {
+ // Message is received after signing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ Bundle data = message.getData();
+ CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
+
+ result.createNotify(ViewKeyActivity.this).show();
+ }
+ }
+ };
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ startActivityForResult(intent, 0);
+ }
+
+ private void showQrCodeDialog() {
+ Intent qrCodeIntent = new Intent(this, QrCodeViewActivity.class);
+
+ // create the transition animation - the images in the layouts
+ // of both activities are defined with android:transitionName="qr_code"
+ Bundle opts = null;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ ActivityOptions options = ActivityOptions
+ .makeSceneTransitionAnimation(this, mQrCodeLayout, "qr_code");
+ opts = options.toBundle();
+ }
+
+ qrCodeIntent.setData(mDataUri);
+ ActivityCompat.startActivity(this, qrCodeIntent, opts);
+ }
+
private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper)
throws ProviderHelper.NotFoundException {
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri);
@@ -258,7 +473,31 @@ public class ViewKeyActivity extends ActionBarActivity implements
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // if a result has been returned, display a notify
+ if (requestCode == REQUEST_QR_FINGERPRINT && resultCode == Activity.RESULT_OK) {
+
+ // If there is an EXTRA_RESULT, that's an error. Just show it.
+ if (data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(this).show();
+ return;
+ }
+
+ String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT);
+ if (fp == null) {
+ Notify.createNotify(this, "Error scanning fingerprint!",
+ Notify.LENGTH_LONG, Notify.Style.ERROR).show();
+ return;
+ }
+ if (mFingerprint.equalsIgnoreCase(fp)) {
+ certifyImmediate();
+ } else {
+ Notify.createNotify(this, "Fingerprints did not match!",
+ Notify.LENGTH_LONG, Notify.Style.ERROR).show();
+ }
+
+ return;
+ }
+
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(this).show();
@@ -267,6 +506,154 @@ public class ViewKeyActivity extends ActionBarActivity implements
}
}
+ private void encrypt(Uri dataUri, boolean text) {
+ // If there is no encryption key, don't bother.
+ if (!mHasEncrypt) {
+ Notify.showNotify(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR);
+ return;
+ }
+ try {
+ long keyId = new ProviderHelper(this)
+ .getCachedPublicKeyRing(dataUri)
+ .extractOrGetMasterKeyId();
+ long[] encryptionKeyIds = new long[]{keyId};
+ Intent intent;
+ if (text) {
+ intent = new Intent(this, EncryptTextActivity.class);
+ intent.setAction(EncryptTextActivity.ACTION_ENCRYPT_TEXT);
+ intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
+ } else {
+ intent = new Intent(this, EncryptFilesActivity.class);
+ intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA);
+ intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
+ }
+ // used instead of startActivity set actionbar based on callingPackage
+ startActivityForResult(intent, 0);
+ } catch (PgpKeyNotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ }
+ }
+
+ private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper)
+ throws ProviderHelper.NotFoundException {
+
+ mIsRefreshing = true;
+ mRefreshItem.setEnabled(false);
+ mRefreshItem.setActionView(mRefresh);
+ mRefresh.startAnimation(mRotate);
+
+ byte[] blob = (byte[]) providerHelper.getGenericData(
+ KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
+
+ ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
+ ArrayList<ParcelableKeyRing> entries = new ArrayList<>();
+ entries.add(keyEntry);
+
+ // Message is received after importing is done in KeychainIntentService
+ KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(this) {
+ 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();
+
+ mIsRefreshing = false;
+
+ if (returnData == null) {
+ finish();
+ return;
+ }
+ final ImportKeyResult result =
+ returnData.getParcelable(OperationResult.EXTRA_RESULT);
+ result.createNotify(ViewKeyActivity.this).show();
+ }
+ }
+ };
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ // search config
+ {
+ Preferences prefs = Preferences.getPreferences(this);
+ Preferences.CloudSearchPrefs cloudPrefs =
+ new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
+ data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ }
+
+ data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, entries);
+
+ // Send all information needed to service to query keys in other thread
+ Intent intent = new Intent(this, KeychainIntentService.class);
+ intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(serviceHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ serviceHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+
+ }
+
+ private void editKey(Uri dataUri) {
+ Intent editIntent = new Intent(this, EditKeyActivity.class);
+ editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri));
+ startActivityForResult(editIntent, 0);
+ }
+
+ private void startSafeSlinger(Uri dataUri) {
+ long keyId = 0;
+ try {
+ keyId = new ProviderHelper(this)
+ .getCachedPublicKeyRing(dataUri)
+ .extractOrGetMasterKeyId();
+ } catch (PgpKeyNotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ }
+ Intent safeSlingerIntent = new Intent(this, SafeSlingerActivity.class);
+ safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, keyId);
+ startActivityForResult(safeSlingerIntent, 0);
+ }
+
+ /**
+ * Load QR Code asynchronously and with a fade in animation
+ */
+ private void loadQrCode(final String fingerprint) {
+ AsyncTask<Void, Void, Bitmap> loadTask =
+ new AsyncTask<Void, Void, Bitmap>() {
+ protected Bitmap doInBackground(Void... unused) {
+ String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ // render with minimal size
+ return QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0);
+ }
+
+ protected void onPostExecute(Bitmap qrCode) {
+ // scale the image up to our actual size. we do this in code rather
+ // than let the ImageView do this because we don't require filtering.
+ Bitmap scaled = Bitmap.createScaledBitmap(qrCode,
+ mQrCode.getHeight(), mQrCode.getHeight(),
+ false);
+ mQrCode.setImageBitmap(scaled);
+
+ // simple fade-in animation
+ AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
+ anim.setDuration(200);
+ mQrCode.startAnimation(anim);
+ }
+ };
+
+ loadTask.execute();
+ }
+
/**
* NFC: Initialize NFC sharing if OS and device supports it
*/
@@ -357,25 +744,34 @@ public class ViewKeyActivity extends ActionBarActivity implements
}
};
- static final String[] UNIFIED_PROJECTION = new String[]{
+ // These are the rows that we will retrieve.
+ static final String[] PROJECTION = new String[]{
KeychainContract.KeyRings._ID,
KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.KeyRings.USER_ID,
KeychainContract.KeyRings.IS_REVOKED,
KeychainContract.KeyRings.EXPIRY,
-
+ KeychainContract.KeyRings.VERIFIED,
+ KeychainContract.KeyRings.HAS_ANY_SECRET,
+ KeychainContract.KeyRings.FINGERPRINT,
+ KeychainContract.KeyRings.HAS_ENCRYPT
};
- static final int INDEX_UNIFIED_MASTER_KEY_ID = 1;
- static final int INDEX_UNIFIED_USER_ID = 2;
- static final int INDEX_UNIFIED_IS_REVOKED = 3;
- static final int INDEX_UNIFIED_EXPIRY = 4;
+
+ static final int INDEX_MASTER_KEY_ID = 1;
+ static final int INDEX_USER_ID = 2;
+ static final int INDEX_IS_REVOKED = 3;
+ static final int INDEX_EXPIRY = 4;
+ static final int INDEX_VERIFIED = 5;
+ static final int INDEX_HAS_ANY_SECRET = 6;
+ static final int INDEX_FINGERPRINT = 7;
+ static final int INDEX_HAS_ENCRYPT = 8;
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
case LOADER_ID_UNIFIED: {
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri);
- return new CursorLoader(this, baseUri, UNIFIED_PROJECTION, null, null, null);
+ return new CursorLoader(this, baseUri, PROJECTION, null, null, null);
}
default:
@@ -383,6 +779,8 @@ public class ViewKeyActivity extends ActionBarActivity implements
}
}
+ int mPreviousColor = 0;
+
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
/* TODO better error handling? May cause problems when a key is deleted,
@@ -398,37 +796,165 @@ public class ViewKeyActivity extends ActionBarActivity implements
case LOADER_ID_UNIFIED: {
if (data.moveToFirst()) {
// get name, email, and comment from USER_ID
- String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_UNIFIED_USER_ID));
+ String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
if (mainUserId[0] != null) {
- setTitle(mainUserId[0]);
+ mName.setText(mainUserId[0]);
} else {
- setTitle(R.string.user_id_no_name);
+ mName.setText(R.string.user_id_no_name);
}
- // get key id from MASTER_KEY_ID
- long masterKeyId = data.getLong(INDEX_UNIFIED_MASTER_KEY_ID);
- getSupportActionBar().setSubtitle(KeyFormattingUtils.beautifyKeyIdWithPrefix(this, masterKeyId));
+ mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
+ mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT));
+
+ mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
+ mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0;
+ boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0;
+ boolean isExpired = !data.isNull(INDEX_EXPIRY)
+ && new Date(data.getLong(INDEX_EXPIRY) * 1000).before(new Date());
+ mIsVerified = data.getInt(INDEX_VERIFIED) > 0;
+
+ // if the refresh animation isn't playing
+ if (!mRotate.hasStarted() && !mRotateSpin.hasStarted()) {
+ // re-create options menu based on mIsSecret, mIsVerified
+ supportInvalidateOptionsMenu();
+ // this is done at the end of the animation otherwise
+ }
- boolean isRevoked = data.getInt(INDEX_UNIFIED_IS_REVOKED) > 0;
- boolean isExpired = !data.isNull(INDEX_UNIFIED_EXPIRY)
- && new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000).before(new Date());
+ AsyncTask<String, Void, Bitmap> photoTask =
+ new AsyncTask<String, Void, Bitmap>() {
+ protected Bitmap doInBackground(String... fingerprint) {
+ return ContactHelper.photoFromFingerprint(getContentResolver(), fingerprint[0]);
+ }
+
+ protected void onPostExecute(Bitmap photo) {
+ mPhoto.setImageBitmap(photo);
+ mPhoto.setVisibility(View.VISIBLE);
+ }
+ };
// Note: order is important
+ int color;
if (isRevoked) {
mStatusText.setText(R.string.view_key_revoked);
- KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, KeyFormattingUtils.STATE_REVOKED);
- mStatusDivider.setVisibility(View.VISIBLE);
- mStatusLayout.setVisibility(View.VISIBLE);
+ mStatusImage.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
+ KeyFormattingUtils.STATE_REVOKED, R.color.icons, true);
+ color = getResources().getColor(R.color.android_red_light);
+
+ mActionEncryptFile.setVisibility(View.GONE);
+ mActionEncryptText.setVisibility(View.GONE);
+ mActionNfc.setVisibility(View.GONE);
+ mFab.setVisibility(View.GONE);
+ mQrCodeLayout.setVisibility(View.GONE);
} else if (isExpired) {
- mStatusText.setText(R.string.view_key_expired);
- KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, KeyFormattingUtils.STATE_EXPIRED);
- mStatusDivider.setVisibility(View.VISIBLE);
- mStatusLayout.setVisibility(View.VISIBLE);
+ if (mIsSecret) {
+ mStatusText.setText(R.string.view_key_expired_secret);
+ } else {
+ mStatusText.setText(R.string.view_key_expired);
+ }
+ mStatusImage.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
+ KeyFormattingUtils.STATE_EXPIRED, R.color.icons, true);
+ color = getResources().getColor(R.color.android_red_light);
+
+ mActionEncryptFile.setVisibility(View.GONE);
+ mActionEncryptText.setVisibility(View.GONE);
+ mActionNfc.setVisibility(View.GONE);
+ mFab.setVisibility(View.GONE);
+ mQrCodeLayout.setVisibility(View.GONE);
+ } else if (mIsSecret) {
+ mStatusText.setText(R.string.view_key_my_key);
+ mStatusImage.setVisibility(View.GONE);
+ color = getResources().getColor(R.color.primary);
+ photoTask.execute(mFingerprint);
+ loadQrCode(mFingerprint);
+ mQrCodeLayout.setVisibility(View.VISIBLE);
+
+ // and place leftOf qr code
+ RelativeLayout.LayoutParams nameParams = (RelativeLayout.LayoutParams)
+ mName.getLayoutParams();
+ // remove right margin
+ nameParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0);
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ nameParams.setMarginEnd(0);
+ }
+ nameParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout);
+ mName.setLayoutParams(nameParams);
+
+ RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams)
+ mStatusText.getLayoutParams();
+ statusParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0);
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ statusParams.setMarginEnd(0);
+ }
+ statusParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout);
+ mStatusText.setLayoutParams(statusParams);
+
+ mActionEncryptFile.setVisibility(View.VISIBLE);
+ mActionEncryptText.setVisibility(View.VISIBLE);
+
+ // invokeBeam is available from API 21
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mActionNfc.setVisibility(View.VISIBLE);
+ } else {
+ mActionNfc.setVisibility(View.GONE);
+ }
+ mFab.setVisibility(View.VISIBLE);
+ mFab.setIconDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp));
} else {
- mStatusDivider.setVisibility(View.GONE);
- mStatusLayout.setVisibility(View.GONE);
+ mActionEncryptFile.setVisibility(View.VISIBLE);
+ mActionEncryptText.setVisibility(View.VISIBLE);
+ mQrCodeLayout.setVisibility(View.GONE);
+ mActionNfc.setVisibility(View.GONE);
+
+ if (mIsVerified) {
+ mStatusText.setText(R.string.view_key_verified);
+ mStatusImage.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
+ KeyFormattingUtils.STATE_VERIFIED, R.color.icons, true);
+ color = getResources().getColor(R.color.primary);
+ photoTask.execute(mFingerprint);
+
+ mFab.setVisibility(View.GONE);
+ } else {
+ mStatusText.setText(R.string.view_key_unverified);
+ mStatusImage.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
+ KeyFormattingUtils.STATE_UNVERIFIED, R.color.icons, true);
+ color = getResources().getColor(R.color.android_orange_light);
+
+ mFab.setVisibility(View.VISIBLE);
+ }
}
+ if (mPreviousColor == 0 || mPreviousColor == color) {
+ mToolbar.setBackgroundColor(color);
+ mStatusBar.setBackgroundColor(color);
+ mBigToolbar.setBackgroundColor(color);
+ mPreviousColor = color;
+ } else {
+ ObjectAnimator colorFade1 =
+ ObjectAnimator.ofObject(mToolbar, "backgroundColor",
+ new ArgbEvaluator(), mPreviousColor, color);
+ ObjectAnimator colorFade2 =
+ ObjectAnimator.ofObject(mStatusBar, "backgroundColor",
+ new ArgbEvaluator(), mPreviousColor, color);
+ ObjectAnimator colorFade3 =
+ ObjectAnimator.ofObject(mBigToolbar, "backgroundColor",
+ new ArgbEvaluator(), mPreviousColor, color);
+
+ colorFade1.setDuration(1200);
+ colorFade2.setDuration(1200);
+ colorFade3.setDuration(1200);
+ colorFade1.start();
+ colorFade2.start();
+ colorFade3.start();
+ mPreviousColor = color;
+ }
+
+ //noinspection deprecation
+ mStatusImage.setAlpha(80);
+
break;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java
new file mode 100644
index 000000000..9d79b377c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2015 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.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.support.v4.view.ViewPager;
+import android.view.View;
+import android.widget.Toast;
+
+import com.astuetz.PagerSlidingTabStrip;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.ContactHelper;
+import org.sufficientlysecure.keychain.util.ExportHelper;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.Date;
+
+public class ViewKeyAdvActivity extends BaseActivity implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ ExportHelper mExportHelper;
+ ProviderHelper mProviderHelper;
+
+ protected Uri mDataUri;
+
+ public static final String EXTRA_SELECTED_TAB = "selected_tab";
+ public static final int TAB_MAIN = 0;
+ public static final int TAB_SHARE = 1;
+
+ // view
+ private ViewPager mViewPager;
+ private PagerSlidingTabStrip mSlidingTabLayout;
+ private PagerTabStripAdapter mTabsAdapter;
+
+ private static final int LOADER_ID_UNIFIED = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setFullScreenDialogClose(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+
+ mExportHelper = new ExportHelper(this);
+ mProviderHelper = new ProviderHelper(this);
+
+ mViewPager = (ViewPager) findViewById(R.id.view_key_pager);
+ mSlidingTabLayout = (PagerSlidingTabStrip) findViewById(R.id.view_key_sliding_tab_layout);
+
+ int switchToTab = TAB_MAIN;
+ Intent intent = getIntent();
+ if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) {
+ switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
+ }
+
+ mDataUri = getIntent().getData();
+ if (mDataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be uri of key!");
+ finish();
+ return;
+ }
+ if (mDataUri.getHost().equals(ContactsContract.AUTHORITY)) {
+ mDataUri = ContactHelper.dataUriFromContactUri(this, mDataUri);
+ if (mDataUri == null) {
+ Log.e(Constants.TAG, "Contact Data missing. Should be uri of key!");
+ Toast.makeText(this, R.string.error_contacts_key_id_missing, Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+ }
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ // Prepare the loaders. Either re-connect with an existing ones,
+ // or start new ones.
+ getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
+
+ initTabs(mDataUri);
+
+ // switch to tab selected by extra
+ mViewPager.setCurrentItem(switchToTab);
+ }
+
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.view_key_adv_activity);
+ }
+
+ private void initTabs(Uri dataUri) {
+ mTabsAdapter = new PagerTabStripAdapter(this);
+ mViewPager.setAdapter(mTabsAdapter);
+
+ Bundle shareBundle = new Bundle();
+ shareBundle.putParcelable(ViewKeyAdvUserIdsFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyAdvShareFragment.class,
+ shareBundle, getString(R.string.key_view_tab_share));
+
+ Bundle userIdsBundle = new Bundle();
+ userIdsBundle.putParcelable(ViewKeyAdvUserIdsFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyAdvUserIdsFragment.class,
+ userIdsBundle, getString(R.string.section_user_ids));
+
+ Bundle keysBundle = new Bundle();
+ keysBundle.putParcelable(ViewKeyAdvSubkeysFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyAdvSubkeysFragment.class,
+ keysBundle, getString(R.string.key_view_tab_keys));
+
+ Bundle certsBundle = new Bundle();
+ certsBundle.putParcelable(ViewKeyAdvCertsFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyAdvCertsFragment.class,
+ certsBundle, getString(R.string.key_view_tab_certs));
+
+ Bundle trustBundle = new Bundle();
+ trustBundle.putParcelable(ViewKeyTrustFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyTrustFragment.class,
+ trustBundle, getString(R.string.key_view_tab_keybase));
+
+ // update layout after operations
+ mSlidingTabLayout.setViewPager(mViewPager);
+ }
+
+ // These are the rows that we will retrieve.
+ static final String[] PROJECTION = new String[]{
+ KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.USER_ID,
+ KeychainContract.KeyRings.IS_REVOKED,
+ KeychainContract.KeyRings.EXPIRY,
+ KeychainContract.KeyRings.VERIFIED,
+ KeychainContract.KeyRings.HAS_ANY_SECRET
+ };
+
+ static final int INDEX_MASTER_KEY_ID = 1;
+ static final int INDEX_USER_ID = 2;
+ static final int INDEX_IS_REVOKED = 3;
+ static final int INDEX_EXPIRY = 4;
+ static final int INDEX_VERIFIED = 5;
+ static final int INDEX_HAS_ANY_SECRET = 6;
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_ID_UNIFIED: {
+ Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(this, baseUri, PROJECTION, null, null, null);
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ /* TODO better error handling? May cause problems when a key is deleted,
+ * because the notification triggers faster than the activity closes.
+ */
+ // Avoid NullPointerExceptions...
+ if (data.getCount() == 0) {
+ return;
+ }
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ switch (loader.getId()) {
+ case LOADER_ID_UNIFIED: {
+ if (data.moveToFirst()) {
+ // get name, email, and comment from USER_ID
+ String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
+ if (mainUserId[0] != null) {
+ setTitle(mainUserId[0]);
+ } else {
+ setTitle(R.string.user_id_no_name);
+ }
+
+ // get key id from MASTER_KEY_ID
+ long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
+ getSupportActionBar().setSubtitle(KeyFormattingUtils.beautifyKeyIdWithPrefix(this, masterKeyId));
+
+ boolean isSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
+ boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0;
+ boolean isExpired = !data.isNull(INDEX_EXPIRY)
+ && new Date(data.getLong(INDEX_EXPIRY) * 1000).before(new Date());
+ boolean isVerified = data.getInt(INDEX_VERIFIED) > 0;
+
+ // Note: order is important
+ int color;
+ if (isRevoked || isExpired) {
+ color = getResources().getColor(R.color.android_red_light);
+ } else if (isSecret) {
+ color = getResources().getColor(R.color.primary);
+ } else {
+ if (isVerified) {
+ color = getResources().getColor(R.color.primary);
+ } else {
+ color = getResources().getColor(R.color.android_orange_light);
+ }
+ }
+ mToolbar.setBackgroundColor(color);
+ mStatusBar.setBackgroundColor(color);
+ mSlidingTabLayout.setBackgroundColor(color);
+
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // if a result has been returned, display a notify
+ if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(this).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvCertsFragment.java
index 61bd126ce..90d7a400f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvCertsFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014-2015 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
@@ -30,7 +30,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
-import android.widget.ListView;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
@@ -39,7 +38,6 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
-import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
@@ -47,23 +45,16 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
-public class ViewKeyAdvancedFragment extends LoaderFragment implements
+public class ViewKeyAdvCertsFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener {
public static final String ARG_DATA_URI = "data_uri";
- private ListView mSubkeysList;
- private SubkeysAdapter mSubkeysAdapter;
-
private StickyListHeadersListView mStickyList;
private CertListAdapter mCertsAdapter;
- private Uri mDataUriSubkeys;
private Uri mDataUriCerts;
- private static final int LOADER_SUBKEYS = 1;
- private static final int LOADER_CERTS = 2;
-
// These are the rows that we will retrieve.
static final String[] CERTS_PROJECTION = new String[]{
KeychainContract.Certs._ID,
@@ -86,8 +77,8 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements
/**
* Creates new instance of this fragment
*/
- public static ViewKeyAdvancedFragment newInstance(Uri dataUri) {
- ViewKeyAdvancedFragment frag = new ViewKeyAdvancedFragment();
+ public static ViewKeyAdvCertsFragment newInstance(Uri dataUri) {
+ ViewKeyAdvCertsFragment frag = new ViewKeyAdvCertsFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_DATA_URI, dataUri);
@@ -99,9 +90,8 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
- View view = inflater.inflate(R.layout.view_key_advanced_fragment, getContainer());
+ View view = inflater.inflate(R.layout.view_key_adv_certs_fragment, getContainer());
- mSubkeysList = (ListView) view.findViewById(R.id.keys);
mStickyList = (StickyListHeadersListView) view.findViewById(R.id.certs_list);
return root;
@@ -122,7 +112,6 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements
}
private void loadData(Uri dataUri) {
- mDataUriSubkeys = KeychainContract.Keys.buildKeysUri(dataUri);
mDataUriCerts = KeychainContract.Certs.buildCertsUri(dataUri);
mStickyList.setAreHeadersSticky(true);
@@ -132,34 +121,23 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements
mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
// Create an empty adapter we will use to display the loaded data.
- mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0);
- mSubkeysList.setAdapter(mSubkeysAdapter);
-
mCertsAdapter = new CertListAdapter(getActivity(), null);
mStickyList.setAdapter(mCertsAdapter);
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
- getLoaderManager().initLoader(LOADER_SUBKEYS, null, this);
- getLoaderManager().initLoader(LOADER_CERTS, null, this);
+ getLoaderManager().initLoader(0, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
setContentShown(false);
- switch (id) {
- case LOADER_SUBKEYS:
- return new CursorLoader(getActivity(), mDataUriSubkeys,
- SubkeysAdapter.SUBKEYS_PROJECTION, null, null, null);
-
- case LOADER_CERTS:
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), mDataUriCerts,
- CERTS_PROJECTION, null, null, CERTS_SORT_ORDER);
-
- default:
- return null;
- }
+
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(getActivity(), mDataUriCerts,
+ CERTS_PROJECTION, null, null, CERTS_SORT_ORDER);
+
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
@@ -170,15 +148,8 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
- switch (loader.getId()) {
- case LOADER_SUBKEYS:
- mSubkeysAdapter.swapCursor(data);
- break;
- case LOADER_CERTS:
- mCertsAdapter.swapCursor(data);
- mStickyList.setAdapter(mCertsAdapter);
- break;
- }
+ mCertsAdapter.swapCursor(data);
+ mStickyList.setAdapter(mCertsAdapter);
// TODO: maybe show not before both are loaded!
setContentShown(true);
@@ -189,14 +160,7 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements
* We need to make sure we are no longer using it.
*/
public void onLoaderReset(Loader<Cursor> loader) {
- switch (loader.getId()) {
- case LOADER_SUBKEYS:
- mSubkeysAdapter.swapCursor(null);
- break;
- case LOADER_CERTS:
- mCertsAdapter.swapCursor(null);
- break;
- }
+ mCertsAdapter.swapCursor(null);
}
/**
@@ -307,7 +271,7 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.view_key_certs_item, parent, false);
+ return mInflater.inflate(R.layout.view_key_adv_certs_item, parent, false);
}
/**
@@ -322,7 +286,7 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements
HeaderViewHolder holder;
if (convertView == null) {
holder = new HeaderViewHolder();
- convertView = mInflater.inflate(R.layout.view_key_certs_header, parent, false);
+ convertView = mInflater.inflate(R.layout.view_key_adv_certs_header, parent, false);
holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text);
holder.count = (TextView) convertView.findViewById(R.id.certs_num);
convertView.setTag(holder);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
index aa260b654..8d0a2dd1d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
@@ -17,7 +17,7 @@
package org.sufficientlysecure.keychain.ui;
-import android.annotation.TargetApi;
+import android.app.ActivityOptions;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -26,10 +26,11 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.provider.Settings;
+import android.support.v4.app.ActivityCompat;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
+import android.support.v7.widget.CardView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -47,7 +48,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
@@ -56,20 +56,19 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
-public class ViewKeyShareFragment extends LoaderFragment implements
+public class ViewKeyAdvShareFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri";
private TextView mFingerprint;
- private ImageView mFingerprintQrCode;
+ private ImageView mQrCode;
+ private CardView mQrCodeLayout;
private View mFingerprintShareButton;
private View mFingerprintClipboardButton;
private View mKeyShareButton;
private View mKeyClipboardButton;
private ImageButton mKeySafeSlingerButton;
- private View mNfcHelpButton;
- private View mNfcPrefsButton;
private View mKeyUploadButton;
ProviderHelper mProviderHelper;
@@ -81,31 +80,24 @@ public class ViewKeyShareFragment extends LoaderFragment implements
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
- View view = inflater.inflate(R.layout.view_key_share_fragment, getContainer());
+ View view = inflater.inflate(R.layout.view_key_adv_share_fragment, getContainer());
- mProviderHelper = new ProviderHelper(ViewKeyShareFragment.this.getActivity());
+ mProviderHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity());
mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint);
- mFingerprintQrCode = (ImageView) view.findViewById(R.id.view_key_fingerprint_qr_code_image);
+ mQrCode = (ImageView) view.findViewById(R.id.view_key_qr_code);
+ mQrCodeLayout = (CardView) view.findViewById(R.id.view_key_qr_code_layout);
mFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share);
mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard);
mKeyShareButton = view.findViewById(R.id.view_key_action_key_share);
mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard);
mKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger);
- mNfcHelpButton = view.findViewById(R.id.view_key_action_nfc_help);
- mNfcPrefsButton = view.findViewById(R.id.view_key_action_nfc_prefs);
mKeyUploadButton = view.findViewById(R.id.view_key_action_upload);
mKeySafeSlingerButton.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
PorterDuff.Mode.SRC_IN);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- mNfcPrefsButton.setVisibility(View.VISIBLE);
- } else {
- mNfcPrefsButton.setVisibility(View.GONE);
- }
-
- mFingerprintQrCode.setOnClickListener(new View.OnClickListener() {
+ mQrCodeLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showQrCodeDialog();
@@ -142,18 +134,6 @@ public class ViewKeyShareFragment extends LoaderFragment implements
startSafeSlinger(mDataUri);
}
});
- mNfcHelpButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- showNfcHelpDialog();
- }
- });
- mNfcPrefsButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- showNfcPrefs();
- }
- });
mKeyUploadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -228,10 +208,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements
}
startActivity(Intent.createChooser(sendIntent, title));
}
- } catch (PgpGeneralException e) {
- Log.e(Constants.TAG, "error processing key!", e);
- Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);
- } catch (IOException e) {
+ } catch (PgpGeneralException | IOException e) {
Log.e(Constants.TAG, "error processing key!", e);
Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);
} catch (ProviderHelper.NotFoundException e) {
@@ -242,20 +219,18 @@ public class ViewKeyShareFragment extends LoaderFragment implements
private void showQrCodeDialog() {
Intent qrCodeIntent = new Intent(getActivity(), QrCodeViewActivity.class);
- qrCodeIntent.setData(mDataUri);
- startActivity(qrCodeIntent);
- }
- private void showNfcHelpDialog() {
- ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance();
- dialog.show(getActivity().getSupportFragmentManager(), "shareNfcDialog");
- }
+ // create the transition animation - the images in the layouts
+ // of both activities are defined with android:transitionName="qr_code"
+ Bundle opts = null;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ ActivityOptions options = ActivityOptions
+ .makeSceneTransitionAnimation(getActivity(), mQrCodeLayout, "qr_code");
+ opts = options.toBundle();
+ }
- @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- private void showNfcPrefs() {
- Intent intentSettings = new Intent(
- Settings.ACTION_NFCSHARING_SETTINGS);
- startActivity(intentSettings);
+ qrCodeIntent.setData(mDataUri);
+ ActivityCompat.startActivity(getActivity(), qrCodeIntent, opts);
}
@Override
@@ -361,19 +336,19 @@ public class ViewKeyShareFragment extends LoaderFragment implements
protected void onPostExecute(Bitmap qrCode) {
// only change view, if fragment is attached to activity
- if (ViewKeyShareFragment.this.isAdded()) {
+ if (ViewKeyAdvShareFragment.this.isAdded()) {
// scale the image up to our actual size. we do this in code rather
// than let the ImageView do this because we don't require filtering.
Bitmap scaled = Bitmap.createScaledBitmap(qrCode,
- mFingerprintQrCode.getHeight(), mFingerprintQrCode.getHeight(),
+ mQrCode.getHeight(), mQrCode.getHeight(),
false);
- mFingerprintQrCode.setImageBitmap(scaled);
+ mQrCode.setImageBitmap(scaled);
// simple fade-in animation
AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
anim.setDuration(200);
- mFingerprintQrCode.startAnimation(anim);
+ mQrCode.startAnimation(anim);
}
}
};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
new file mode 100644
index 000000000..bd00c6780
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014-2015 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.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+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.ViewGroup;
+import android.widget.ListView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ public static final String ARG_DATA_URI = "data_uri";
+
+ private ListView mSubkeysList;
+ private SubkeysAdapter mSubkeysAdapter;
+
+ private Uri mDataUriSubkeys;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static ViewKeyAdvSubkeysFragment newInstance(Uri dataUri) {
+ ViewKeyAdvSubkeysFragment frag = new ViewKeyAdvSubkeysFragment();
+
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_DATA_URI, dataUri);
+
+ frag.setArguments(args);
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.view_key_adv_subkeys_fragment, getContainer());
+
+ mSubkeysList = (ListView) view.findViewById(R.id.keys);
+
+ return root;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ getActivity().finish();
+ return;
+ }
+
+ loadData(dataUri);
+ }
+
+ private void loadData(Uri dataUri) {
+ mDataUriSubkeys = KeychainContract.Keys.buildKeysUri(dataUri);
+
+ // Create an empty adapter we will use to display the loaded data.
+ mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0);
+ mSubkeysList.setAdapter(mSubkeysAdapter);
+
+ // Prepare the loaders. Either re-connect with an existing ones,
+ // or start new ones.
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ setContentShown(false);
+
+ return new CursorLoader(getActivity(), mDataUriSubkeys,
+ SubkeysAdapter.SUBKEYS_PROJECTION, null, null, null);
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ // Avoid NullPointerExceptions, if we get an empty result set.
+ if (data.getCount() == 0) {
+ return;
+ }
+
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ mSubkeysAdapter.swapCursor(data);
+
+ // TODO: maybe show not before both are loaded!
+ setContentShown(true);
+ }
+
+ /**
+ * 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.
+ */
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mSubkeysAdapter.swapCursor(null);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java
new file mode 100644
index 000000000..c4e6639a8
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * 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.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+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.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
+import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ public static final String ARG_DATA_URI = "uri";
+
+ private ListView mUserIds;
+
+ private static final int LOADER_ID_UNIFIED = 0;
+ private static final int LOADER_ID_USER_IDS = 1;
+
+ private UserIdsAdapter mUserIdsAdapter;
+
+ private Uri mDataUri;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.view_key_adv_main_fragment, getContainer());
+
+ mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
+
+ mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ showUserIdInfo(position);
+ }
+ });
+
+ return root;
+ }
+
+ private void showUserIdInfo(final int position) {
+ final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position);
+ final int isVerified = mUserIdsAdapter.getIsVerified(position);
+
+ DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
+ public void run() {
+ UserIdInfoDialogFragment dialogFragment =
+ UserIdInfoDialogFragment.newInstance(isRevoked, isVerified);
+
+ dialogFragment.show(getActivity().getSupportFragmentManager(), "userIdInfoDialog");
+ }
+ });
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ getActivity().finish();
+ return;
+ }
+
+ loadData(dataUri);
+ }
+
+ private void loadData(Uri dataUri) {
+ mDataUri = dataUri;
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0);
+ mUserIds.setAdapter(mUserIdsAdapter);
+
+ // Prepare the loaders. Either re-connect with an existing ones,
+ // or start new ones.
+ getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
+ getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
+ }
+
+ static final String[] UNIFIED_PROJECTION = new String[]{
+ KeyRings._ID, KeyRings.MASTER_KEY_ID,
+ KeyRings.HAS_ANY_SECRET, KeyRings.IS_REVOKED, KeyRings.EXPIRY, KeyRings.HAS_ENCRYPT
+ };
+ static final int INDEX_UNIFIED_MASTER_KEY_ID = 1;
+ static final int INDEX_UNIFIED_HAS_ANY_SECRET = 2;
+ static final int INDEX_UNIFIED_IS_REVOKED = 3;
+ static final int INDEX_UNIFIED_EXPIRY = 4;
+ static final int INDEX_UNIFIED_HAS_ENCRYPT = 5;
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ setContentShown(false);
+
+ switch (id) {
+ case LOADER_ID_UNIFIED: {
+ Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
+ }
+ case LOADER_ID_USER_IDS: {
+ Uri baseUri = UserPackets.buildUserIdsUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri,
+ UserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ /* TODO better error handling? May cause problems when a key is deleted,
+ * because the notification triggers faster than the activity closes.
+ */
+ // Avoid NullPointerExceptions...
+ if (data.getCount() == 0) {
+ return;
+ }
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ switch (loader.getId()) {
+ case LOADER_ID_UNIFIED: {
+ if (data.moveToFirst()) {
+
+
+ break;
+ }
+ }
+
+ case LOADER_ID_USER_IDS: {
+ mUserIdsAdapter.swapCursor(data);
+ break;
+ }
+
+ }
+ setContentShown(true);
+ }
+
+ /**
+ * 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.
+ */
+ public void onLoaderReset(Loader<Cursor> loader) {
+ switch (loader.getId()) {
+ case LOADER_ID_USER_IDS:
+ mUserIdsAdapter.swapCursor(null);
+ break;
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java
new file mode 100644
index 000000000..453bfd499
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * 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.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+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.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.Date;
+
+public class ViewKeyFragment extends LoaderFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ public static final String ARG_DATA_URI = "uri";
+
+ private ListView mUserIds;
+
+ boolean mIsSecret = false;
+
+ private static final int LOADER_ID_UNIFIED = 0;
+ private static final int LOADER_ID_USER_IDS = 1;
+
+ private UserIdsAdapter mUserIdsAdapter;
+
+ private Uri mDataUri;
+
+ ProviderHelper mProviderHelper;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static ViewKeyFragment newInstance(Uri dataUri) {
+ ViewKeyFragment frag = new ViewKeyFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_DATA_URI, dataUri);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.view_key_fragment, getContainer());
+
+ mProviderHelper = new ProviderHelper(getActivity());
+
+ mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
+
+ mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ showUserIdInfo(position);
+ }
+ });
+
+ return root;
+ }
+
+ private void showUserIdInfo(final int position) {
+ if (!mIsSecret) {
+ final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position);
+ final int isVerified = mUserIdsAdapter.getIsVerified(position);
+
+ DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
+ public void run() {
+ UserIdInfoDialogFragment dialogFragment =
+ UserIdInfoDialogFragment.newInstance(isRevoked, isVerified);
+
+ dialogFragment.show(getActivity().getSupportFragmentManager(), "userIdInfoDialog");
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ getActivity().finish();
+ return;
+ }
+
+ loadData(dataUri);
+ }
+
+
+ // These are the rows that we will retrieve.
+ static final String[] UNIFIED_PROJECTION = new String[]{
+ KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.USER_ID,
+ KeychainContract.KeyRings.IS_REVOKED,
+ KeychainContract.KeyRings.EXPIRY,
+ KeychainContract.KeyRings.VERIFIED,
+ KeychainContract.KeyRings.HAS_ANY_SECRET,
+ KeychainContract.KeyRings.FINGERPRINT,
+ KeychainContract.KeyRings.HAS_ENCRYPT
+ };
+
+ static final int INDEX_MASTER_KEY_ID = 1;
+ static final int INDEX_USER_ID = 2;
+ static final int INDEX_IS_REVOKED = 3;
+ static final int INDEX_EXPIRY = 4;
+ static final int INDEX_VERIFIED = 5;
+ static final int INDEX_HAS_ANY_SECRET = 6;
+ static final int INDEX_FINGERPRINT = 7;
+ static final int INDEX_HAS_ENCRYPT = 8;
+
+ private void loadData(Uri dataUri) {
+ mDataUri = dataUri;
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ // Prepare the loaders. Either re-connect with an existing ones,
+ // or start new ones.
+ getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
+ }
+
+ // don't show revoked user ids here, irrelevant for average users
+ public static final String USER_IDS_WHERE = UserPackets.IS_REVOKED + " = 0";
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ setContentShown(false);
+
+ switch (id) {
+ case LOADER_ID_UNIFIED: {
+ Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
+ }
+ case LOADER_ID_USER_IDS: {
+ Uri baseUri = UserPackets.buildUserIdsUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri,
+ UserIdsAdapter.USER_IDS_PROJECTION, USER_IDS_WHERE, null, null);
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ /* TODO better error handling? May cause problems when a key is deleted,
+ * because the notification triggers faster than the activity closes.
+ */
+ // Avoid NullPointerExceptions...
+ if (data.getCount() == 0) {
+ return;
+ }
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ switch (loader.getId()) {
+ case LOADER_ID_UNIFIED: {
+ if (data.moveToFirst()) {
+
+ mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
+ boolean hasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0;
+ boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0;
+ boolean isExpired = !data.isNull(INDEX_EXPIRY)
+ && new Date(data.getLong(INDEX_EXPIRY) * 1000).before(new Date());
+ boolean isVerified = data.getInt(INDEX_VERIFIED) > 0;
+
+ // load user ids after we know if it's a secret key
+ mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, false, !mIsSecret, null);
+ mUserIds.setAdapter(mUserIdsAdapter);
+ getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
+
+ break;
+ }
+ }
+
+ case LOADER_ID_USER_IDS: {
+ mUserIdsAdapter.swapCursor(data);
+ break;
+ }
+
+ }
+ setContentShown(true);
+ }
+
+ /**
+ * 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.
+ */
+ public void onLoaderReset(Loader<Cursor> loader) {
+ switch (loader.getId()) {
+ case LOADER_ID_USER_IDS: {
+ mUserIdsAdapter.swapCursor(null);
+ break;
+ }
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
deleted file mode 100644
index 8101006e1..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
- *
- * 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.content.Intent;
-import android.database.Cursor;
-import android.graphics.PorterDuff;
-import android.net.Uri;
-import android.os.Bundle;
-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.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ImageView;
-import android.widget.ListView;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
-import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
-import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
-import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.ui.util.Notify;
-
-import java.util.Date;
-
-public class ViewKeyMainFragment extends LoaderFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
-
- public static final String ARG_DATA_URI = "uri";
-
- private View mActionLink, mActionLinkDivider;
- private View mActionEdit;
- private View mActionEditDivider;
- private View mActionEncryptFiles;
- private View mActionEncryptText;
- private View mActionEncryptTextText;
- private View mActionCertify;
- private View mActionCertifyText;
- private ImageView mActionCertifyImage;
- private View mActionUpdate;
-
- private ListView mUserIds;
-
- private static final int LOADER_ID_UNIFIED = 0;
- private static final int LOADER_ID_USER_IDS = 1;
-
- // conservative attitude
- private boolean mHasEncrypt = true;
-
- private UserIdsAdapter mUserIdsAdapter;
-
- private Uri mDataUri;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
- View root = super.onCreateView(inflater, superContainer, savedInstanceState);
- View view = inflater.inflate(R.layout.view_key_main_fragment, getContainer());
-
- mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
- mActionLink = view.findViewById(R.id.view_key_action_link);
- mActionLinkDivider = view.findViewById(R.id.view_key_action_link);
- mActionEdit = view.findViewById(R.id.view_key_action_edit);
- mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider);
- mActionEncryptText = view.findViewById(R.id.view_key_action_encrypt_text);
- mActionEncryptTextText = view.findViewById(R.id.view_key_action_encrypt_text_text);
- mActionEncryptFiles = view.findViewById(R.id.view_key_action_encrypt_files);
- mActionCertify = view.findViewById(R.id.view_key_action_certify);
- mActionCertifyText = view.findViewById(R.id.view_key_action_certify_text);
- mActionCertifyImage = (ImageView) view.findViewById(R.id.view_key_action_certify_image);
- // make certify image gray, like action icons
- mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
- PorterDuff.Mode.SRC_IN);
- mActionUpdate = view.findViewById(R.id.view_key_action_update);
-
- mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- showUserIdInfo(position);
- }
- });
-
- return root;
- }
-
- private void showUserIdInfo(final int position) {
- final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position);
- final int isVerified = mUserIdsAdapter.getIsVerified(position);
-
- DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
- public void run() {
- UserIdInfoDialogFragment dialogFragment =
- UserIdInfoDialogFragment.newInstance(isRevoked, isVerified);
-
- dialogFragment.show(getActivity().getSupportFragmentManager(), "userIdInfoDialog");
- }
- });
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
- if (dataUri == null) {
- Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
- getActivity().finish();
- return;
- }
-
- loadData(dataUri);
- }
-
- private void loadData(Uri dataUri) {
- mDataUri = dataUri;
-
- Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
-
- mActionEncryptFiles.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- encrypt(mDataUri, false);
- }
- });
- mActionEncryptText.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- encrypt(mDataUri, true);
- }
- });
- mActionCertify.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- certify(mDataUri);
- }
- });
- mActionLink.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- linkKey(mDataUri);
- }
- });
- mActionEdit.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- editKey(mDataUri);
- }
- });
- mActionUpdate.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- try {
- updateFromKeyserver(mDataUri, new ProviderHelper(getActivity()));
- } catch (NotFoundException e) {
- Notify.showNotify(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR);
- }
- }
- });
-
- mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0);
- mUserIds.setAdapter(mUserIdsAdapter);
-
- // Prepare the loaders. Either re-connect with an existing ones,
- // or start new ones.
- getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
- getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
- }
-
- static final String[] UNIFIED_PROJECTION = new String[]{
- KeyRings._ID, KeyRings.MASTER_KEY_ID,
- KeyRings.HAS_ANY_SECRET, KeyRings.IS_REVOKED, KeyRings.EXPIRY, KeyRings.HAS_ENCRYPT
- };
- static final int INDEX_UNIFIED_MASTER_KEY_ID = 1;
- static final int INDEX_UNIFIED_HAS_ANY_SECRET = 2;
- static final int INDEX_UNIFIED_IS_REVOKED = 3;
- static final int INDEX_UNIFIED_EXPIRY = 4;
- static final int INDEX_UNIFIED_HAS_ENCRYPT = 5;
-
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- setContentShown(false);
-
- switch (id) {
- case LOADER_ID_UNIFIED: {
- Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
- return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
- }
- case LOADER_ID_USER_IDS: {
- Uri baseUri = UserPackets.buildUserIdsUri(mDataUri);
- return new CursorLoader(getActivity(), baseUri,
- UserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
- }
-
- default:
- return null;
- }
- }
-
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- /* TODO better error handling? May cause problems when a key is deleted,
- * because the notification triggers faster than the activity closes.
- */
- // Avoid NullPointerExceptions...
- if (data.getCount() == 0) {
- return;
- }
- // Swap the new cursor in. (The framework will take care of closing the
- // old cursor once we return.)
- switch (loader.getId()) {
- case LOADER_ID_UNIFIED: {
- if (data.moveToFirst()) {
- if (data.getInt(INDEX_UNIFIED_HAS_ANY_SECRET) != 0) {
- // edit button
- mActionLink.setVisibility(View.VISIBLE);
- mActionLinkDivider.setVisibility(View.VISIBLE);
- mActionEdit.setVisibility(View.VISIBLE);
- mActionEditDivider.setVisibility(View.VISIBLE);
- } else {
- // edit button
- mActionLink.setVisibility(View.GONE);
- mActionLinkDivider.setVisibility(View.GONE);
- mActionEdit.setVisibility(View.GONE);
- mActionEditDivider.setVisibility(View.GONE);
- }
-
- // If this key is revoked, it cannot be used for anything!
- if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) {
- mActionEdit.setEnabled(false);
- mActionCertify.setEnabled(false);
- mActionCertifyText.setEnabled(false);
- mActionEncryptText.setEnabled(false);
- mActionEncryptTextText.setEnabled(false);
- mActionEncryptFiles.setEnabled(false);
- } else {
- mActionEdit.setEnabled(true);
-
- Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);
- if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) {
- mActionCertify.setEnabled(false);
- mActionCertifyText.setEnabled(false);
- mActionEncryptText.setEnabled(false);
- mActionEncryptTextText.setEnabled(false);
- mActionEncryptFiles.setEnabled(false);
- } else {
- mActionCertify.setEnabled(true);
- mActionCertifyText.setEnabled(true);
- mActionEncryptText.setEnabled(true);
- mActionEncryptTextText.setEnabled(true);
- mActionEncryptFiles.setEnabled(true);
- }
- }
-
- mHasEncrypt = data.getInt(INDEX_UNIFIED_HAS_ENCRYPT) != 0;
-
- break;
- }
- }
-
- case LOADER_ID_USER_IDS:
- mUserIdsAdapter.swapCursor(data);
- break;
-
- }
- setContentShown(true);
- }
-
- /**
- * 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.
- */
- public void onLoaderReset(Loader<Cursor> loader) {
- switch (loader.getId()) {
- case LOADER_ID_USER_IDS:
- mUserIdsAdapter.swapCursor(null);
- break;
- }
- }
-
- private void encrypt(Uri dataUri, boolean text) {
- // If there is no encryption key, don't bother.
- if (!mHasEncrypt) {
- Notify.showNotify(getActivity(), R.string.error_no_encrypt_subkey, Notify.Style.ERROR);
- return;
- }
- try {
- long keyId = new ProviderHelper(getActivity())
- .getCachedPublicKeyRing(dataUri)
- .extractOrGetMasterKeyId();
- long[] encryptionKeyIds = new long[]{keyId};
- Intent intent;
- if (text) {
- intent = new Intent(getActivity(), EncryptTextActivity.class);
- intent.setAction(EncryptTextActivity.ACTION_ENCRYPT_TEXT);
- intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
- } else {
- intent = new Intent(getActivity(), EncryptFilesActivity.class);
- intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA);
- intent.putExtra(EncryptFilesActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
- }
- // used instead of startActivity set actionbar based on callingPackage
- startActivityForResult(intent, 0);
- } catch (PgpKeyNotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- }
- }
-
- private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper)
- throws ProviderHelper.NotFoundException {
- byte[] blob = (byte[]) providerHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
- KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
- String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
-
- Intent queryIntent = new Intent(getActivity(), ImportKeysActivity.class);
- queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT);
- queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint);
-
- startActivityForResult(queryIntent, 0);
- }
-
- private void certify(Uri dataUri) {
- long keyId = 0;
- try {
- keyId = new ProviderHelper(getActivity())
- .getCachedPublicKeyRing(dataUri)
- .extractOrGetMasterKeyId();
- } catch (PgpKeyNotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- }
- Intent certifyIntent = new Intent(getActivity(), CertifyKeyActivity.class);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{keyId});
- startActivityForResult(certifyIntent, 0);
- }
-
- private void editKey(Uri dataUri) {
- Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
- editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri));
- startActivityForResult(editIntent, 0);
- }
-
- private void linkKey(Uri dataUri) {
- Intent editIntent = new Intent(getActivity(), LinkedIdWizard.class);
- editIntent.setData(KeyRings.buildUnifiedKeyRingUri(dataUri));
- startActivityForResult(editIntent, 0);
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
new file mode 100644
index 000000000..25edc7a02
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2014 Tim Bray <tbray@textuality.com>
+ *
+ * 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.app.ProgressDialog;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+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.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.text.style.StyleSpan;
+import android.text.style.URLSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+import com.textuality.keybase.lib.KeybaseException;
+import com.textuality.keybase.lib.Proof;
+import com.textuality.keybase.lib.User;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.List;
+
+public class ViewKeyTrustFragment extends LoaderFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ public static final String ARG_DATA_URI = "uri";
+
+ private View mStartSearch;
+ private TextView mTrustReadout;
+ private TextView mReportHeader;
+ private TableLayout mProofListing;
+ private LayoutInflater mInflater;
+ private View mProofVerifyHeader;
+ private TextView mProofVerifyDetail;
+
+ private static final int LOADER_ID_DATABASE = 1;
+
+ // for retrieving the key we’re working on
+ private Uri mDataUri;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.view_key_adv_keybase_fragment, getContainer());
+ mInflater = inflater;
+
+ mTrustReadout = (TextView) view.findViewById(R.id.view_key_trust_readout);
+ mStartSearch = view.findViewById(R.id.view_key_trust_search_cloud);
+ mStartSearch.setEnabled(false);
+ mReportHeader = (TextView) view.findViewById(R.id.view_key_trust_cloud_narrative);
+ mProofListing = (TableLayout) view.findViewById(R.id.view_key_proof_list);
+ mProofVerifyHeader = view.findViewById(R.id.view_key_proof_verify_header);
+ mProofVerifyDetail = (TextView) view.findViewById(R.id.view_key_proof_verify_detail);
+ mReportHeader.setVisibility(View.GONE);
+ mProofListing.setVisibility(View.GONE);
+ mProofVerifyHeader.setVisibility(View.GONE);
+ mProofVerifyDetail.setVisibility(View.GONE);
+
+ return root;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ getActivity().finish();
+ return;
+ }
+ mDataUri = dataUri;
+
+ // retrieve the key from the database
+ getLoaderManager().initLoader(LOADER_ID_DATABASE, null, this);
+ }
+
+ static final String[] TRUST_PROJECTION = new String[]{
+ KeyRings._ID, KeyRings.FINGERPRINT, KeyRings.IS_REVOKED, KeyRings.EXPIRY,
+ KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED
+ };
+ static final int INDEX_TRUST_FINGERPRINT = 1;
+ static final int INDEX_TRUST_IS_REVOKED = 2;
+ static final int INDEX_TRUST_EXPIRY = 3;
+ static final int INDEX_UNIFIED_HAS_ANY_SECRET = 4;
+ static final int INDEX_VERIFIED = 5;
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ setContentShown(false);
+
+ switch (id) {
+ case LOADER_ID_DATABASE: {
+ Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, TRUST_PROJECTION, null, null, null);
+ }
+ // decided to just use an AsyncTask for keybase, but maybe later
+ default:
+ return null;
+ }
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ /* TODO better error handling? May cause problems when a key is deleted,
+ * because the notification triggers faster than the activity closes.
+ */
+ // Avoid NullPointerExceptions...
+ if (data.getCount() == 0) {
+ return;
+ }
+
+ boolean nothingSpecial = true;
+ StringBuilder message = new StringBuilder();
+
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ if (data.moveToFirst()) {
+
+ if (data.getInt(INDEX_UNIFIED_HAS_ANY_SECRET) != 0) {
+ message.append(getString(R.string.key_trust_it_is_yours)).append("\n");
+ nothingSpecial = false;
+ } else if (data.getInt(INDEX_VERIFIED) != 0) {
+ message.append(getString(R.string.key_trust_already_verified)).append("\n");
+ nothingSpecial = false;
+ }
+
+ // If this key is revoked, don’t trust it!
+ if (data.getInt(INDEX_TRUST_IS_REVOKED) != 0) {
+ message.append(getString(R.string.key_trust_revoked)).
+ append(getString(R.string.key_trust_old_keys));
+
+ nothingSpecial = false;
+ } else {
+ Date expiryDate = new Date(data.getLong(INDEX_TRUST_EXPIRY) * 1000);
+ if (!data.isNull(INDEX_TRUST_EXPIRY) && expiryDate.before(new Date())) {
+
+ // if expired, don’t trust it!
+ message.append(getString(R.string.key_trust_expired)).
+ append(getString(R.string.key_trust_old_keys));
+
+ nothingSpecial = false;
+ }
+ }
+
+ if (nothingSpecial) {
+ message.append(getString(R.string.key_trust_maybe));
+ }
+
+ final byte[] fp = data.getBlob(INDEX_TRUST_FINGERPRINT);
+ final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fp);
+ if (fingerprint != null) {
+
+ mStartSearch.setEnabled(true);
+ mStartSearch.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mStartSearch.setEnabled(false);
+ new DescribeKey().execute(fingerprint);
+ }
+ });
+ }
+ }
+
+ mTrustReadout.setText(message);
+ setContentShown(true);
+ }
+
+ /**
+ * 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.
+ */
+ public void onLoaderReset(Loader<Cursor> loader) {
+ // no-op in this case I think
+ }
+
+ class ResultPage {
+ String mHeader;
+ final List<CharSequence> mProofs;
+
+ public ResultPage(String header, List<CharSequence> proofs) {
+ mHeader = header;
+ mProofs = proofs;
+ }
+ }
+
+ // look for evidence from keybase in the background, make tabular version of result
+ //
+ private class DescribeKey extends AsyncTask<String, Void, ResultPage> {
+
+ @Override
+ protected ResultPage doInBackground(String... args) {
+ String fingerprint = args[0];
+
+ final ArrayList<CharSequence> proofList = new ArrayList<CharSequence>();
+ final Hashtable<Integer, ArrayList<Proof>> proofs = new Hashtable<Integer, ArrayList<Proof>>();
+ try {
+ User keybaseUser = User.findByFingerprint(fingerprint);
+ for (Proof proof : keybaseUser.getProofs()) {
+ Integer proofType = proof.getType();
+ appendIfOK(proofs, proofType, proof);
+ }
+
+ // a one-liner in a modern programming language
+ for (Integer proofType : proofs.keySet()) {
+ Proof[] x = {};
+ Proof[] proofsFor = proofs.get(proofType).toArray(x);
+ if (proofsFor.length > 0) {
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+ ssb.append(getProofNarrative(proofType)).append(" ");
+
+ int i = 0;
+ while (i < proofsFor.length - 1) {
+ appendProofLinks(ssb, fingerprint, proofsFor[i]);
+ ssb.append(", ");
+ i++;
+ }
+ appendProofLinks(ssb, fingerprint, proofsFor[i]);
+ proofList.add(ssb);
+ }
+ }
+
+ } catch (KeybaseException ignored) {
+ }
+
+ return new ResultPage(getString(R.string.key_trust_results_prefix), proofList);
+ }
+
+ private SpannableStringBuilder appendProofLinks(SpannableStringBuilder ssb, final String fingerprint, final Proof proof) throws KeybaseException {
+ int startAt = ssb.length();
+ String handle = proof.getHandle();
+ ssb.append(handle);
+ ssb.setSpan(new URLSpan(proof.getServiceUrl()), startAt, startAt + handle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (haveProofFor(proof.getType())) {
+ ssb.append("\u00a0[");
+ startAt = ssb.length();
+ String verify = getString(R.string.keybase_verify);
+ ssb.append(verify);
+ ClickableSpan clicker = new ClickableSpan() {
+ @Override
+ public void onClick(View view) {
+ verify(proof, fingerprint);
+ }
+ };
+ ssb.setSpan(clicker, startAt, startAt + verify.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append("]");
+ }
+ return ssb;
+ }
+
+ @Override
+ protected void onPostExecute(ResultPage result) {
+ super.onPostExecute(result);
+ if (result.mProofs.isEmpty()) {
+ result.mHeader = getActivity().getString(R.string.key_trust_no_cloud_evidence);
+ }
+
+ mStartSearch.setVisibility(View.GONE);
+ mReportHeader.setVisibility(View.VISIBLE);
+ mProofListing.setVisibility(View.VISIBLE);
+ mReportHeader.setText(result.mHeader);
+
+ int rowNumber = 1;
+ for (CharSequence s : result.mProofs) {
+ TableRow row = (TableRow) mInflater.inflate(R.layout.view_key_adv_keybase_proof, null);
+ TextView number = (TextView) row.findViewById(R.id.proof_number);
+ TextView text = (TextView) row.findViewById(R.id.proof_text);
+ number.setText(Integer.toString(rowNumber++) + ". ");
+ text.setText(s);
+ text.setMovementMethod(LinkMovementMethod.getInstance());
+ mProofListing.addView(row);
+ }
+
+ // mSearchReport.loadDataWithBaseURL("file:///android_res/drawable/", s, "text/html", "UTF-8", null);
+ }
+ }
+
+ private String getProofNarrative(int proofType) {
+ int stringIndex;
+ switch (proofType) {
+ case Proof.PROOF_TYPE_TWITTER: stringIndex = R.string.keybase_narrative_twitter; break;
+ case Proof.PROOF_TYPE_GITHUB: stringIndex = R.string.keybase_narrative_github; break;
+ case Proof.PROOF_TYPE_DNS: stringIndex = R.string.keybase_narrative_dns; break;
+ case Proof.PROOF_TYPE_WEB_SITE: stringIndex = R.string.keybase_narrative_web_site; break;
+ case Proof.PROOF_TYPE_HACKERNEWS: stringIndex = R.string.keybase_narrative_hackernews; break;
+ case Proof.PROOF_TYPE_COINBASE: stringIndex = R.string.keybase_narrative_coinbase; break;
+ case Proof.PROOF_TYPE_REDDIT: stringIndex = R.string.keybase_narrative_reddit; break;
+ default: stringIndex = R.string.keybase_narrative_unknown;
+ }
+ return getActivity().getString(stringIndex);
+ }
+
+ private void appendIfOK(Hashtable<Integer, ArrayList<Proof>> table, Integer proofType, Proof proof) throws KeybaseException {
+ ArrayList<Proof> list = table.get(proofType);
+ if (list == null) {
+ list = new ArrayList<Proof>();
+ table.put(proofType, list);
+ }
+ list.add(proof);
+ }
+
+ // which proofs do we have working verifiers for?
+ private boolean haveProofFor(int proofType) {
+ switch (proofType) {
+ case Proof.PROOF_TYPE_TWITTER: return true;
+ case Proof.PROOF_TYPE_GITHUB: return true;
+ case Proof.PROOF_TYPE_DNS: return true;
+ case Proof.PROOF_TYPE_WEB_SITE: return true;
+ case Proof.PROOF_TYPE_HACKERNEWS: return true;
+ case Proof.PROOF_TYPE_COINBASE: return true;
+ case Proof.PROOF_TYPE_REDDIT: return true;
+ default: return false;
+ }
+ }
+
+ private void verify(final Proof proof, final String fingerprint) {
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+ Bundle data = new Bundle();
+ intent.setAction(KeychainIntentService.ACTION_VERIFY_KEYBASE_PROOF);
+
+ data.putString(KeychainIntentService.KEYBASE_PROOF, proof.toString());
+ data.putString(KeychainIntentService.KEYBASE_REQUIRED_FINGERPRINT, fingerprint);
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ mProofVerifyDetail.setVisibility(View.GONE);
+
+ // Create a new Messenger for the communication back after proof work is done
+ //
+ KeychainIntentServiceHandler handler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_verifying_signature), ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ Bundle returnData = message.getData();
+ String msg = returnData.getString(KeychainIntentServiceHandler.DATA_MESSAGE);
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+
+ if ((msg != null) && msg.equals("OK")) {
+
+ //yay
+ String proofUrl = returnData.getString(KeychainIntentServiceHandler.KEYBASE_PROOF_URL);
+ String presenceUrl = returnData.getString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_URL);
+ String presenceLabel = returnData.getString(KeychainIntentServiceHandler.KEYBASE_PRESENCE_LABEL);
+
+ String proofLabel;
+ switch (proof.getType()) {
+ case Proof.PROOF_TYPE_TWITTER:
+ proofLabel = getString(R.string.keybase_twitter_proof);
+ break;
+ case Proof.PROOF_TYPE_DNS:
+ proofLabel = getString(R.string.keybase_dns_proof);
+ break;
+ case Proof.PROOF_TYPE_WEB_SITE:
+ proofLabel = getString(R.string.keybase_web_site_proof);
+ break;
+ case Proof.PROOF_TYPE_GITHUB:
+ proofLabel = getString(R.string.keybase_github_proof);
+ break;
+ case Proof.PROOF_TYPE_REDDIT:
+ proofLabel = getString(R.string.keybase_reddit_proof);
+ break;
+ default:
+ proofLabel = getString(R.string.keybase_a_post);
+ break;
+ }
+
+ ssb.append(getString(R.string.keybase_proof_succeeded));
+ StyleSpan bold = new StyleSpan(Typeface.BOLD);
+ ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append("\n\n");
+ int length = ssb.length();
+ ssb.append(proofLabel);
+ if (proofUrl != null) {
+ URLSpan postLink = new URLSpan(proofUrl);
+ ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (Proof.PROOF_TYPE_DNS == proof.getType()) {
+ ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
+ } else {
+ ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
+ }
+ length = ssb.length();
+ URLSpan presenceLink = new URLSpan(presenceUrl);
+ ssb.append(presenceLabel);
+ ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
+ ssb.append(", ").
+ append(getString(R.string.keybase_reddit_attribution)).
+ append(" “").append(proof.getHandle()).append("”, ");
+ }
+ ssb.append(" ").append(getString(R.string.keybase_contained_signature));
+ } else {
+ // verification failed!
+ msg = returnData.getString(KeychainIntentServiceHandler.DATA_ERROR);
+ ssb.append(getString(R.string.keybase_proof_failure));
+ if (msg == null) {
+ msg = getString(R.string.keybase_unknown_proof_failure);
+ }
+ StyleSpan bold = new StyleSpan(Typeface.BOLD);
+ ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append("\n\n").append(msg);
+ }
+ mProofVerifyHeader.setVisibility(View.VISIBLE);
+ mProofVerifyDetail.setVisibility(View.VISIBLE);
+ mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
+ mProofVerifyDetail.setText(ssb);
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(handler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ handler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index 6c81e9193..6ba9e26ad 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -95,8 +95,8 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
* @see org.sufficientlysecure.keychain.operations.ImportExportOperation
*/
public ArrayList<ImportKeysListEntry> getSelectedEntries() {
- ArrayList<ImportKeysListEntry> result = new ArrayList<ImportKeysListEntry>();
- ArrayList<ImportKeysListEntry> secrets = new ArrayList<ImportKeysListEntry>();
+ ArrayList<ImportKeysListEntry> result = new ArrayList<>();
+ ArrayList<ImportKeysListEntry> secrets = new ArrayList<>();
if (mData == null) {
return result;
}
@@ -175,9 +175,9 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
}
if (entry.isRevoked()) {
- KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_REVOKED, true);
+ KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray);
} else if (entry.isExpired()) {
- KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_EXPIRED, true);
+ KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray);
}
if (entry.isRevoked() || entry.isExpired()) {
@@ -213,8 +213,24 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
// destroyLoader view from holder
holder.userIdsList.removeAllViews();
+ // we want conventional gpg UserIDs first, then Keybase ”proofs”
HashMap<String, HashSet<String>> mergedUserIds = entry.getMergedUserIds();
- for (Map.Entry<String, HashSet<String>> pair : mergedUserIds.entrySet()) {
+ ArrayList<Map.Entry<String, HashSet<String>>> sortedIds = new ArrayList<Map.Entry<String, HashSet<String>>>(mergedUserIds.entrySet());
+ java.util.Collections.sort(sortedIds, new java.util.Comparator<Map.Entry<String, HashSet<String>>>() {
+ @Override
+ public int compare(Map.Entry<String, HashSet<String>> entry1, Map.Entry<String, HashSet<String>> entry2) {
+
+ // sort keybase UserIds after non-Keybase
+ boolean e1IsKeybase = entry1.getKey().contains(":");
+ boolean e2IsKeybase = entry2.getKey().contains(":");
+ if (e1IsKeybase != e2IsKeybase) {
+ return (e1IsKeybase) ? 1 : -1;
+ }
+ return entry1.getKey().compareTo(entry2.getKey());
+ }
+ });
+
+ for (Map.Entry<String, HashSet<String>> pair : sortedIds) {
String cUserId = pair.getKey();
HashSet<String> cEmails = pair.getValue();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java
index 176c3ff5b..4781864dd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java
@@ -21,13 +21,13 @@ import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.keyimport.CloudSearch;
+import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.util.Preferences;
-import org.sufficientlysecure.keychain.keyimport.CloudSearch;
-import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
@@ -39,7 +39,7 @@ public class ImportKeysListCloudLoader
Preferences.CloudSearchPrefs mCloudPrefs;
String mServerQuery;
- private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<ImportKeysListEntry>();
+ private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<>();
private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs) {
@@ -51,7 +51,7 @@ public class ImportKeysListCloudLoader
@Override
public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
+ mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, null);
if (mServerQuery == null) {
Log.e(Constants.TAG, "mServerQuery is null!");
@@ -119,7 +119,7 @@ public class ImportKeysListCloudLoader
mEntryList.addAll(searchResult);
}
GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null);
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, getKeyResult);
+ mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, getKeyResult);
} catch (Keyserver.CloudSearchFailureException e) {
// convert exception to result parcel
int error = GetKeyResult.RESULT_ERROR;
@@ -140,7 +140,7 @@ public class ImportKeysListCloudLoader
OperationResult.OperationLog log = new OperationResult.OperationLog();
log.add(logType, 0);
GetKeyResult getKeyResult = new GetKeyResult(error, log);
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, getKeyResult);
+ mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, getKeyResult);
}
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
index cecad2716..5447c5f96 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -24,9 +24,9 @@ import android.support.v4.util.LongSparseArray;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -35,28 +35,15 @@ import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Iterator;
public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
- public static class NonPgpPartException extends Exception {
- private int mCount;
-
- public NonPgpPartException(int count) {
- this.mCount = count;
- }
-
- public int getCount() {
- return mCount;
- }
- }
-
final Context mContext;
final InputData mInputData;
- ArrayList<ImportKeysListEntry> mData = new ArrayList<ImportKeysListEntry>();
- LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<ParcelableKeyRing>();
+ ArrayList<ImportKeysListEntry> mData = new ArrayList<>();
+ LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<>();
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
public ImportKeysListLoader(Context context, InputData inputData) {
@@ -73,7 +60,7 @@ public class ImportKeysListLoader
}
GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null);
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, getKeyResult);
+ mEntryListWrapper = new AsyncTaskResultWrapper<>(mData, getKeyResult);
if (mInputData == null) {
Log.e(Constants.TAG, "Input data is null!");
@@ -140,7 +127,7 @@ public class ImportKeysListLoader
OperationResult.OperationLog log = new OperationResult.OperationLog();
log.add(OperationResult.LogType.MSG_GET_NO_VALID_KEYS, 0);
GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_ERROR_NO_VALID_KEYS, log);
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
+ mEntryListWrapper = new AsyncTaskResultWrapper<>
(mData, getKeyResult);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
index 80d605fb9..b8fe21941 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
@@ -33,7 +33,7 @@ public class KeyValueSpinnerAdapter extends ArrayAdapter<String> {
static <K, V extends Comparable<? super V>> SortedSet<Map.Entry<K, V>> entriesSortedByValues(
Map<K, V> map) {
- SortedSet<Map.Entry<K, V>> sortedEntries = new TreeSet<Map.Entry<K, V>>(
+ SortedSet<Map.Entry<K, V>> sortedEntries = new TreeSet<>(
new Comparator<Map.Entry<K, V>>() {
@Override
public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
index 9f29826ef..015775669 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
@@ -21,13 +21,13 @@ import android.content.Context;
import android.database.Cursor;
import android.os.Parcel;
import android.support.v4.util.LongSparseArray;
+import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
-import android.support.v4.widget.CursorAdapter;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
@@ -44,7 +44,7 @@ public class MultiUserIdsAdapter extends CursorAdapter {
public MultiUserIdsAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
- mCheckStates = new ArrayList<Boolean>();
+ mCheckStates = new ArrayList<>();
}
@Override
@@ -148,7 +148,7 @@ public class MultiUserIdsAdapter extends CursorAdapter {
}
public ArrayList<CertifyAction> getSelectedCertifyActions() {
- LongSparseArray<CertifyAction> actions = new LongSparseArray<CertifyAction>();
+ LongSparseArray<CertifyAction> actions = new LongSparseArray<>();
for (int i = 0; i < mCheckStates.size(); i++) {
if (mCheckStates.get(i)) {
mCursor.moveToPosition(i);
@@ -171,7 +171,7 @@ public class MultiUserIdsAdapter extends CursorAdapter {
}
}
- ArrayList<CertifyAction> result = new ArrayList<CertifyAction>(actions.size());
+ ArrayList<CertifyAction> result = new ArrayList<>(actions.size());
for (int i = 0; i < actions.size(); i++) {
result.add(actions.valueAt(i));
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
index 330254a8f..963e77fe9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
@@ -27,7 +27,7 @@ import java.util.ArrayList;
public class PagerTabStripAdapter extends FragmentPagerAdapter {
protected final Activity mActivity;
- protected final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+ protected final ArrayList<TabInfo> mTabs = new ArrayList<>();
static final class TabInfo {
public final Class<?> clss;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
index 60c130969..a836b35df 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
@@ -30,9 +30,9 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.util.Highlighter;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
/**
@@ -133,11 +133,11 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
boolean enabled;
if (cursor.getInt(mIndexIsRevoked) != 0) {
h.statusIcon.setVisibility(View.VISIBLE);
- KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_REVOKED, true);
+ KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray);
enabled = false;
} else if (cursor.getInt(mIndexIsExpiry) != 0) {
h.statusIcon.setVisibility(View.VISIBLE);
- KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_EXPIRED, true);
+ KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray);
enabled = false;
} else {
h.statusIcon.setVisibility(View.GONE);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
index a032e96fc..ff5fbb49a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
@@ -35,11 +35,11 @@ import android.widget.ImageView;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.util.Calendar;
import java.util.Date;
@@ -160,7 +160,11 @@ public class SubkeysAdapter extends CursorAdapter {
cursor.getString(INDEX_KEY_CURVE_OID)
));
- if (mSaveKeyringParcel != null && mSaveKeyringParcel.mStripSubKeys.contains(keyId)) {
+ SubkeyChange change = mSaveKeyringParcel != null
+ ? mSaveKeyringParcel.getSubkeyChange(keyId)
+ : null;
+
+ if (change != null && change.mDummyStrip) {
algorithmStr.append(", ");
final SpannableString boldStripped = new SpannableString(
context.getString(R.string.key_stripped)
@@ -268,12 +272,12 @@ public class SubkeysAdapter extends CursorAdapter {
PorterDuff.Mode.SRC_IN);
if (isRevoked) {
- vStatus.setImageResource(R.drawable.status_signature_revoked_cutout);
+ vStatus.setImageResource(R.drawable.status_signature_revoked_cutout_24px);
vStatus.setColorFilter(
mContext.getResources().getColor(R.color.bg_gray),
PorterDuff.Mode.SRC_IN);
} else if (isExpired) {
- vStatus.setImageResource(R.drawable.status_signature_expired_cutout);
+ vStatus.setImageResource(R.drawable.status_signature_expired_cutout_24px);
vStatus.setColorFilter(
mContext.getResources().getColor(R.color.bg_gray),
PorterDuff.Mode.SRC_IN);
@@ -297,7 +301,7 @@ public class SubkeysAdapter extends CursorAdapter {
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- View view = mInflater.inflate(R.layout.view_key_subkey_item, null);
+ View view = mInflater.inflate(R.layout.view_key_adv_subkey_item, null);
if (mDefaultTextColor == null) {
TextView keyId = (TextView) view.findViewById(R.id.subkey_item_key_id);
mDefaultTextColor = keyId.getTextColors();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
index 9f5d5341e..d2359a387 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
@@ -31,8 +31,8 @@ import android.widget.TextView;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.util.Calendar;
import java.util.Date;
@@ -68,7 +68,7 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// Not recycled, inflate a new view
- convertView = mInflater.inflate(R.layout.view_key_subkey_item, null);
+ convertView = mInflater.inflate(R.layout.view_key_adv_subkey_item, null);
final ViewHolder holder = new ViewHolder();
holder.vKeyId = (TextView) convertView.findViewById(R.id.subkey_item_key_id);
holder.vKeyDetails = (TextView) convertView.findViewById(R.id.subkey_item_details);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
index 9ddfa90be..44afed351 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
@@ -33,7 +33,7 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.
private final Context mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
- private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+ private final ArrayList<TabInfo> mTabs = new ArrayList<>();
static final class TabInfo {
public final Class<?> clss;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java
index 7b8670f22..73c42b1be 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java
@@ -34,6 +34,9 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -43,6 +46,7 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView.
private LayoutInflater mInflater;
private final ArrayList<Boolean> mCheckStates;
private SaveKeyringParcel mSaveKeyringParcel;
+ private boolean mShowStatusImages;
public static final String[] USER_PACKETS_PROJECTION = new String[]{
UserPackets._ID,
@@ -64,12 +68,13 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView.
private static final int INDEX_IS_REVOKED = 7;
public UserAttributesAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes,
- SaveKeyringParcel saveKeyringParcel) {
+ boolean showStatusImages, SaveKeyringParcel saveKeyringParcel) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
mCheckStates = showCheckBoxes ? new ArrayList<Boolean>() : null;
mSaveKeyringParcel = saveKeyringParcel;
+ mShowStatusImages = showStatusImages;
}
public UserAttributesAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) {
@@ -161,12 +166,17 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView.
vVerifiedLayout.setVisibility(View.GONE);
} else {
vEditImage.setVisibility(View.GONE);
- vVerifiedLayout.setVisibility(View.VISIBLE);
+
+ if (mShowStatusImages) {
+ vVerifiedLayout.setVisibility(View.VISIBLE);
+ } else {
+ vVerifiedLayout.setVisibility(View.GONE);
+ }
}
if (isRevoked) {
// set revocation icon (can this even be primary?)
- KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, true);
+ KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray);
// disable revoked user ids
vName.setEnabled(false);
@@ -188,13 +198,13 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView.
int isVerified = cursor.getInt(INDEX_VERIFIED);
switch (isVerified) {
case Certs.VERIFIED_SECRET:
- KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, false);
+ KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
case Certs.VERIFIED_SELF:
- KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, false);
+ KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR);
break;
default:
- KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, false);
+ KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, KeyFormattingUtils.DEFAULT_COLOR);
break;
}
}
@@ -225,7 +235,7 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView.
}
public ArrayList<String> getSelectedUserIds() {
- ArrayList<String> result = new ArrayList<String>();
+ ArrayList<String> result = new ArrayList<>();
for (int i = 0; i < mCheckStates.size(); i++) {
if (mCheckStates.get(i)) {
mCursor.moveToPosition(i);
@@ -267,7 +277,7 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView.
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- View view = mInflater.inflate(R.layout.view_key_user_id_item, null);
+ View view = mInflater.inflate(R.layout.view_key_adv_user_id_item, null);
// only need to do this once ever, since mShowCheckBoxes is final
view.findViewById(R.id.user_id_item_check_box).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE);
return view;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java
index 4c0e7a492..01218a4e4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java
@@ -64,7 +64,7 @@ public class UserIdsAddedAdapter extends ArrayAdapter<String> {
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// Not recycled, inflate a new view
- convertView = mInflater.inflate(R.layout.view_key_user_id_item, null);
+ convertView = mInflater.inflate(R.layout.view_key_adv_user_id_item, null);
final ViewHolder holder = new ViewHolder();
holder.vAddress = (TextView) convertView.findViewById(R.id.user_id_item_address);
holder.vName = (TextView) convertView.findViewById(R.id.user_id_item_name);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
index b4119a5eb..d5376cbdc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
@@ -145,20 +145,20 @@ public class AddSubkeyDialogFragment extends DialogFragment {
}
{
- ArrayList<Choice<Algorithm>> choices = new ArrayList<Choice<Algorithm>>();
- choices.add(new Choice<Algorithm>(Algorithm.DSA, getResources().getString(
+ ArrayList<Choice<Algorithm>> choices = new ArrayList<>();
+ choices.add(new Choice<>(Algorithm.DSA, getResources().getString(
R.string.dsa)));
if (!mWillBeMasterKey) {
- choices.add(new Choice<Algorithm>(Algorithm.ELGAMAL, getResources().getString(
+ choices.add(new Choice<>(Algorithm.ELGAMAL, getResources().getString(
R.string.elgamal)));
}
- choices.add(new Choice<Algorithm>(Algorithm.RSA, getResources().getString(
+ choices.add(new Choice<>(Algorithm.RSA, getResources().getString(
R.string.rsa)));
- choices.add(new Choice<Algorithm>(Algorithm.ECDSA, getResources().getString(
+ choices.add(new Choice<>(Algorithm.ECDSA, getResources().getString(
R.string.ecdsa)));
- choices.add(new Choice<Algorithm>(Algorithm.ECDH, getResources().getString(
+ choices.add(new Choice<>(Algorithm.ECDH, getResources().getString(
R.string.ecdh)));
- ArrayAdapter<Choice<Algorithm>> adapter = new ArrayAdapter<Choice<Algorithm>>(context,
+ ArrayAdapter<Choice<Algorithm>> adapter = new ArrayAdapter<>(context,
android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mAlgorithmSpinner.setAdapter(adapter);
@@ -172,20 +172,20 @@ public class AddSubkeyDialogFragment extends DialogFragment {
}
// dynamic ArrayAdapter must be created (instead of ArrayAdapter.getFromResource), because it's content may change
- ArrayAdapter<CharSequence> keySizeAdapter = new ArrayAdapter<CharSequence>(context, android.R.layout.simple_spinner_item,
+ ArrayAdapter<CharSequence> keySizeAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item,
new ArrayList<CharSequence>(Arrays.asList(getResources().getStringArray(R.array.rsa_key_size_spinner_values))));
keySizeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mKeySizeSpinner.setAdapter(keySizeAdapter);
mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length
{
- ArrayList<Choice<Curve>> choices = new ArrayList<Choice<Curve>>();
+ ArrayList<Choice<Curve>> choices = new ArrayList<>();
- choices.add(new Choice<Curve>(Curve.NIST_P256, getResources().getString(
+ choices.add(new Choice<>(Curve.NIST_P256, getResources().getString(
R.string.key_curve_nist_p256)));
- choices.add(new Choice<Curve>(Curve.NIST_P384, getResources().getString(
+ choices.add(new Choice<>(Curve.NIST_P384, getResources().getString(
R.string.key_curve_nist_p384)));
- choices.add(new Choice<Curve>(Curve.NIST_P521, getResources().getString(
+ choices.add(new Choice<>(Curve.NIST_P521, getResources().getString(
R.string.key_curve_nist_p521)));
/* @see SaveKeyringParcel
@@ -197,7 +197,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {
R.string.key_curve_bp_p512)));
*/
- ArrayAdapter<Choice<Curve>> adapter = new ArrayAdapter<Choice<Curve>>(context,
+ ArrayAdapter<Choice<Curve>> adapter = new ArrayAdapter<>(context,
android.R.layout.simple_spinner_item, choices);
mCurveSpinner.setAdapter(adapter);
// make NIST P-256 the default
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java
index 3eef04aa7..094c4d8a9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java
@@ -45,8 +45,8 @@ import android.widget.TextView.OnEditorActionListener;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.ContactHelper;
import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.util.ContactHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.regex.Matcher;
@@ -85,7 +85,7 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
String predefinedName = getArguments().getString(ARG_NAME);
- ArrayAdapter<String> autoCompleteEmailAdapter = new ArrayAdapter<String>
+ ArrayAdapter<String> autoCompleteEmailAdapter = new ArrayAdapter<>
(getActivity(), android.R.layout.simple_spinner_dropdown_item,
ContactHelper.getPossibleUserEmails(getActivity())
);
@@ -150,7 +150,7 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA
mName.setThreshold(1); // Start working from first character
mName.setAdapter(
- new ArrayAdapter<String>
+ new ArrayAdapter<>
(getActivity(), android.R.layout.simple_spinner_dropdown_item,
ContactHelper.getPossibleUserNames(getActivity())
)
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java
index 19cf27259..d2fa37cf7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 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
@@ -25,43 +25,48 @@ import android.support.v4.app.FragmentActivity;
import org.sufficientlysecure.keychain.R;
-public class BadImportKeyDialogFragment extends DialogFragment {
- private static final String ARG_BAD_IMPORT = "bad_import";
+public class AdvancedAppSettingsDialogFragment extends DialogFragment {
+ private static final String ARG_PACKAGE_NAME = "package_name";
+ private static final String ARG_SIGNATURE = "signature";
/**
- * Creates a new instance of this Bad Import Key DialogFragment
- *
- * @param bad
- * @return
+ * Creates new instance of this fragment
*/
- public static BadImportKeyDialogFragment newInstance(int bad) {
- BadImportKeyDialogFragment frag = new BadImportKeyDialogFragment();
+ public static AdvancedAppSettingsDialogFragment newInstance(String packageName, String digest) {
+ AdvancedAppSettingsDialogFragment frag = new AdvancedAppSettingsDialogFragment();
Bundle args = new Bundle();
+ args.putString(ARG_PACKAGE_NAME, packageName);
+ args.putString(ARG_SIGNATURE, digest);
- args.putInt(ARG_BAD_IMPORT, bad);
frag.setArguments(args);
-
return frag;
}
+ /**
+ * Creates dialog
+ */
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
- final int badImport = getArguments().getInt(ARG_BAD_IMPORT);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
- alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
- alert.setTitle(R.string.warning);
- alert.setMessage(activity.getResources()
- .getQuantityString(R.plurals.bad_keys_encountered, badImport, badImport));
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- });
+
+ alert.setTitle(R.string.api_settings_advanced);
alert.setCancelable(true);
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dismiss();
+ }
+ });
+
+ String packageName = getArguments().getString(ARG_PACKAGE_NAME);
+ String signature = getArguments().getString(ARG_SIGNATURE);
+
+ alert.setMessage(getString(R.string.api_settings_package_name) + ": " + packageName + "\n\n"
+ + getString(R.string.api_settings_package_signature) + ": " + signature);
+
return alert.show();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java
index aa27e8391..d405b1dda 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java
@@ -26,13 +26,13 @@ public class CustomAlertDialogBuilder extends AlertDialog.Builder {
int dividerId = dialog.getContext().getResources().getIdentifier("android:id/titleDivider", null, null);
View divider = dialog.findViewById(dividerId);
if (divider != null) {
- divider.setBackgroundColor(dialog.getContext().getResources().getColor(R.color.emphasis));
+ divider.setBackgroundColor(dialog.getContext().getResources().getColor(R.color.header_text));
}
int textViewId = dialog.getContext().getResources().getIdentifier("android:id/alertTitle", null, null);
TextView tv = (TextView) dialog.findViewById(textViewId);
if (tv != null) {
- tv.setTextColor(dialog.getContext().getResources().getColor(R.color.emphasis));
+ tv.setTextColor(dialog.getContext().getResources().getColor(R.color.header_text));
}
return dialog;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
index 42e21cd57..879e3f6da 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Dialog;
+import android.content.ContentResolver;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.Build;
@@ -30,6 +31,8 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.FileHelper;
+import java.io.File;
+
public class DeleteFileDialogFragment extends DialogFragment {
private static final String ARG_DELETE_URI = "delete_uri";
@@ -69,21 +72,38 @@ public class DeleteFileDialogFragment extends DialogFragment {
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
+ String scheme = deleteUri.getScheme();
- // We can not securely delete Uris, so just use usual delete on them
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- if (DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri)) {
+ if(scheme.equals(ContentResolver.SCHEME_FILE)) {
+ if(new File(deleteUri.getPath()).delete()) {
Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show();
return;
}
}
+ else if(scheme.equals(ContentResolver.SCHEME_CONTENT)) {
+ // We can not securely delete Uris, so just use usual delete on them
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ if (DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri)) {
+ Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
- if (getActivity().getContentResolver().delete(deleteUri, null, null) > 0) {
- Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show();
- return;
+ if (getActivity().getContentResolver().delete(deleteUri, null, null) > 0) {
+ Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // some Uri's a ContentResolver fails to delete is handled by the java.io.File's delete
+ // via the path of the Uri
+ if(new File(deleteUri.getPath()).delete()) {
+ Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show();
+ return;
+ }
}
- Toast.makeText(getActivity(), getActivity().getString(R.string.error_file_delete_failed, deleteFilename), Toast.LENGTH_SHORT).show();
+ Toast.makeText(getActivity(), getActivity().getString(R.string.error_file_delete_failed,
+ deleteFilename), Toast.LENGTH_SHORT).show();
// Note: We can't delete every file...
// If possible we should find out if deletion is possible before even showing the option to do so.
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 5b96ea231..802f0c11b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -33,6 +33,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
@@ -100,14 +101,20 @@ public class DeleteKeyDialogFragment extends DialogFragment {
ProviderHelper.FIELD_TYPE_INTEGER
}
);
- String userId = (String) data.get(KeyRings.USER_ID);
+ String name;
+ String[] mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID));
+ if (mainUserId[0] != null) {
+ name = mainUserId[0];
+ } else {
+ name = getString(R.string.user_id_no_name);
+ }
hasSecret = ((Long) data.get(KeyRings.HAS_ANY_SECRET)) == 1;
// Set message depending on which key it is.
mMainMessage.setText(getString(
hasSecret ? R.string.secret_key_deletion_confirmation
: R.string.public_key_deletetion_confirmation,
- userId
+ name
));
} catch (ProviderHelper.NotFoundException e) {
dismiss();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
index 1cc800b2b..7ac85781f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
@@ -37,9 +37,9 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.ui.util.Notify;
import java.io.File;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
deleted file mode 100644
index 961f92f03..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
+++ /dev/null
@@ -1,97 +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.ui.dialog;
-
-import android.annotation.TargetApi;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.nfc.NfcAdapter;
-import android.os.Build;
-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 {
-
- /**
- * Creates new instance of this fragment
- */
- public static ShareNfcDialogFragment newInstance() {
- ShareNfcDialogFragment frag = new ShareNfcDialogFragment();
-
- return frag;
- }
-
- /**
- * Creates dialog
- */
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final FragmentActivity activity = getActivity();
-
- CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
-
- alert.setTitle(R.string.share_nfc_dialog);
- alert.setCancelable(true);
-
- alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- dismiss();
- }
- });
-
- HtmlTextView textView = new HtmlTextView(getActivity());
- textView.setPadding(8, 8, 8, 8);
- alert.setView(textView);
-
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
- textView.setText(getString(R.string.error) + ": "
- + getString(R.string.error_jelly_bean_needed));
- } else {
- // check if NFC Adapter is available
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
- if (nfcAdapter == null) {
- textView.setText(getString(R.string.error) + ": "
- + getString(R.string.error_nfc_needed));
- } else {
- // nfc works...
- textView.setHtmlFromRawResource(getActivity(), R.raw.nfc_beam_share, true);
-
- alert.setNegativeButton(R.string.menu_beam_preferences,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- Intent intentSettings = new Intent(
- Settings.ACTION_NFCSHARING_SETTINGS);
- startActivity(intentSettings);
- }
- }
- );
- }
- }
-
- return alert.show();
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ActionBarHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ActionBarHelper.java
deleted file mode 100644
index edd12ec73..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ActionBarHelper.java
+++ /dev/null
@@ -1,99 +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.ui.util;
-
-import android.app.Activity;
-import android.support.v7.app.ActionBar;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import org.sufficientlysecure.keychain.R;
-
-public class ActionBarHelper {
-
- /**
- * Sets custom view on ActionBar for Done/Cancel activities
- *
- * @param actionBar
- * @param firstText
- * @param firstDrawableId
- * @param firstOnClickListener
- * @param secondText
- * @param secondDrawableId
- * @param secondOnClickListener
- */
- public static void setTwoButtonView(ActionBar actionBar,
- int firstText, int firstDrawableId, OnClickListener firstOnClickListener,
- int secondText, int secondDrawableId, OnClickListener secondOnClickListener) {
-
- // 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 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(
- 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(
- secondOnClickListener);
-
- // Show the custom action bar view and hide the normal Home icon and title.
- actionBar.setDisplayShowTitleEnabled(false);
- actionBar.setDisplayShowHomeEnabled(false);
- actionBar.setDisplayShowCustomEnabled(true);
- actionBar.setCustomView(customActionBarView, new ActionBar.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- }
-
- /**
- * Sets custom view on ActionBar for Done activities
- *
- * @param actionBar
- * @param firstText
- * @param firstOnClickListener
- */
- 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 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(
- firstOnClickListener);
-
- // Show the custom action bar view and hide the normal Home icon and title.
- actionBar.setDisplayShowTitleEnabled(false);
- actionBar.setDisplayShowHomeEnabled(false);
- actionBar.setDisplayShowCustomEnabled(true);
- actionBar.setCustomView(customActionBarView);
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java
index 3f84bf490..eb5c3df45 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java
@@ -24,18 +24,12 @@ import android.text.style.StrikethroughSpan;
public class FormattingUtils {
- public static SpannableStringBuilder strikeOutText(CharSequence text) {
- SpannableStringBuilder sb = new SpannableStringBuilder(text);
- sb.setSpan(new StrikethroughSpan(), 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- return sb;
- }
-
public static int dpToPx(Context context, int dp) {
- return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5);
+ return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5f);
}
public static int pxToDp(Context context, int px) {
- return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5);
+ return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5f);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
index bff7d6b27..38ed88b9c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
@@ -377,6 +377,8 @@ public class KeyFormattingUtils {
((int) digest[2] + 256) % 256};
}
+ public static final int DEFAULT_COLOR = -1;
+
public static final int STATE_REVOKED = 1;
public static final int STATE_EXPIRED = 2;
public static final int STATE_VERIFIED = 3;
@@ -393,20 +395,32 @@ public class KeyFormattingUtils {
}
public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText, int state) {
- setStatusImage(context, statusIcon, statusText, state, false);
+ setStatusImage(context, statusIcon, statusText, state, KeyFormattingUtils.DEFAULT_COLOR, false);
+ }
+
+ public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText,
+ int state, int color) {
+ setStatusImage(context, statusIcon, statusText, state, color, false);
}
/**
* Sets status image based on constant
*/
public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText,
- int state, boolean unobtrusive) {
+ int state, int color, boolean big) {
switch (state) {
/** GREEN: everything is good **/
case STATE_VERIFIED: {
- statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_verified_cutout));
- int color = R.color.android_green_light;
+ if (big) {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_verified_cutout_96px));
+ } else {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_verified_cutout_24px));
+ }
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.android_green_light;
+ }
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
if (statusText != null) {
@@ -416,8 +430,10 @@ public class KeyFormattingUtils {
}
case STATE_ENCRYPTED: {
statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_lock_closed));
- int color = R.color.android_green_light;
+ context.getResources().getDrawable(R.drawable.status_lock_closed_24px));
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.android_green_light;
+ }
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
if (statusText != null) {
@@ -427,9 +443,16 @@ public class KeyFormattingUtils {
}
/** ORANGE: mostly bad... **/
case STATE_UNVERIFIED: {
- statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout));
- int color = R.color.android_orange_light;
+ if (big) {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout_96px));
+ } else {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout_24px));
+ }
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.android_orange_light;
+ }
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
if (statusText != null) {
@@ -439,8 +462,10 @@ public class KeyFormattingUtils {
}
case STATE_UNKNOWN_KEY: {
statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout));
- int color = R.color.android_orange_light;
+ context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24px));
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.android_orange_light;
+ }
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
if (statusText != null) {
@@ -450,11 +475,15 @@ public class KeyFormattingUtils {
}
/** RED: really bad... **/
case STATE_REVOKED: {
- statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout));
- int color = R.color.android_red_light;
- if (unobtrusive) {
- color = R.color.bg_gray;
+ if (big) {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout_96px));
+ } else {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout_24px));
+ }
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.android_red_light;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -464,11 +493,15 @@ public class KeyFormattingUtils {
break;
}
case STATE_EXPIRED: {
- statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_expired_cutout));
- int color = R.color.android_red_light;
- if (unobtrusive) {
- color = R.color.bg_gray;
+ if (big) {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_expired_cutout_96px));
+ } else {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_expired_cutout_24px));
+ }
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.android_red_light;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -479,8 +512,10 @@ public class KeyFormattingUtils {
}
case STATE_NOT_ENCRYPTED: {
statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_lock_open));
- int color = R.color.android_red_light;
+ context.getResources().getDrawable(R.drawable.status_lock_open_24px));
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.android_red_light;
+ }
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
if (statusText != null) {
@@ -490,8 +525,10 @@ public class KeyFormattingUtils {
}
case STATE_NOT_SIGNED: {
statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout));
- int color = R.color.android_red_light;
+ context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24px));
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.android_red_light;
+ }
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
if (statusText != null) {
@@ -501,8 +538,10 @@ public class KeyFormattingUtils {
}
case STATE_INVALID: {
statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout));
- int color = R.color.android_red_light;
+ context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24px));
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.android_red_light;
+ }
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
if (statusText != null) {
@@ -513,8 +552,10 @@ public class KeyFormattingUtils {
/** special **/
case STATE_UNAVAILABLE: {
statusIcon.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout));
- int color = R.color.bg_gray;
+ context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24px));
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.bg_gray;
+ }
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
if (statusText != null) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java
index 551ac039d..3bc29edb6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java
@@ -20,46 +20,135 @@ package org.sufficientlysecure.keychain.ui.util;
import android.app.Activity;
import android.content.res.Resources;
-import com.github.johnpersano.supertoasts.SuperCardToast;
-import com.github.johnpersano.supertoasts.SuperToast;
+import com.nispok.snackbar.Snackbar;
+import com.nispok.snackbar.Snackbar.SnackbarDuration;
+import com.nispok.snackbar.SnackbarManager;
+import com.nispok.snackbar.enums.SnackbarType;
+import com.nispok.snackbar.listeners.ActionClickListener;
+import com.nispok.snackbar.listeners.EventListenerAdapter;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.FabContainer;
/**
- * @author danielhass
* Notify wrapper which allows a more easy use of different notification libraries
*/
public class Notify {
public static enum Style {OK, WARN, INFO, ERROR}
+ public static final int LENGTH_INDEFINITE = 0;
+ public static final int LENGTH_LONG = 3500;
+
/**
* Shows a simple in-layout notification with the CharSequence given as parameter
- * @param activity
* @param text Text to show
* @param style Notification styling
*/
- public static void showNotify(Activity activity, CharSequence text, Style style) {
+ public static void showNotify(final Activity activity, CharSequence text, Style style) {
+
+ Snackbar bar = getSnackbar(activity)
+ .text(text);
+
+ switch (style) {
+ case OK:
+ break;
+ case WARN:
+ bar.textColor(activity.getResources().getColor(R.color.android_orange_light));
+ break;
+ case ERROR:
+ bar.textColor(activity.getResources().getColor(R.color.android_red_light));
+ break;
+ }
+
+ SnackbarManager.show(bar);
+
+ }
+
+ public static Showable createNotify (Activity activity, int resId, int duration, Style style) {
+ final Snackbar bar = getSnackbar(activity)
+ .text(resId);
+
+ if (duration == LENGTH_INDEFINITE) {
+ bar.duration(SnackbarDuration.LENGTH_INDEFINITE);
+ } else {
+ bar.duration(duration);
+ }
+
+ switch (style) {
+ case OK:
+ bar.actionColor(activity.getResources().getColor(R.color.android_green_light));
+ break;
+ case WARN:
+ bar.textColor(activity.getResources().getColor(R.color.android_orange_light));
+ break;
+ case ERROR:
+ bar.textColor(activity.getResources().getColor(R.color.android_red_light));
+ break;
+ }
- SuperCardToast st = new SuperCardToast(activity);
- st.setText(text);
- st.setDuration(SuperToast.Duration.MEDIUM);
- switch (style){
+ return new Showable () {
+ @Override
+ public void show() {
+ SnackbarManager.show(bar);
+ }
+ };
+ }
+
+ public static Showable createNotify(Activity activity, int resId, int duration, Style style,
+ final ActionListener listener, int resIdAction) {
+ return createNotify(activity, activity.getString(resId), duration, style, listener, resIdAction);
+ }
+
+ public static Showable createNotify(Activity activity, String msg, int duration, Style style) {
+ return createNotify(activity, msg, duration, style, null, 0);
+ }
+
+ public static Showable createNotify(Activity activity, String msg, int duration, Style style,
+ final ActionListener listener, int resIdAction) {
+
+ final Snackbar bar = getSnackbar(activity)
+ .text(msg);
+
+ if (listener != null) {
+ bar.actionLabel(resIdAction);
+ bar.actionListener(new ActionClickListener() {
+ @Override
+ public void onActionClicked(Snackbar snackbar) {
+ listener.onAction();
+ }
+ });
+ }
+
+ if (duration == LENGTH_INDEFINITE) {
+ bar.duration(SnackbarDuration.LENGTH_INDEFINITE);
+ } else {
+ bar.duration(duration);
+ }
+
+ switch (style) {
case OK:
- st.setBackground(SuperToast.Background.GREEN);
+ bar.actionColor(activity.getResources().getColor(R.color.android_green_light));
break;
case WARN:
- st.setBackground(SuperToast.Background.ORANGE);
+ bar.textColor(activity.getResources().getColor(R.color.android_orange_light));
break;
case ERROR:
- st.setBackground(SuperToast.Background.RED);
+ bar.textColor(activity.getResources().getColor(R.color.android_red_light));
break;
}
- st.show();
+
+ return new Showable () {
+ @Override
+ public void show() {
+ SnackbarManager.show(bar);
+ }
+ };
}
/**
* Shows a simple in-layout notification with the resource text from given id
- * @param activity
* @param resId ResourceId of notification text
* @param style Notification styling
* @throws Resources.NotFoundException
@@ -67,4 +156,36 @@ public class Notify {
public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException {
showNotify(activity, activity.getResources().getText(resId), style);
}
+
+ private static Snackbar getSnackbar(final Activity activity) {
+ Snackbar bar = Snackbar.with(activity)
+ .type(SnackbarType.MULTI_LINE)
+ .duration(SnackbarDuration.LENGTH_LONG);
+
+ if (activity instanceof FabContainer) {
+ bar.eventListener(new EventListenerAdapter() {
+ @Override
+ public void onShow(Snackbar snackbar) {
+ ((FabContainer) activity).fabMoveUp(snackbar.getHeight());
+ }
+
+ @Override
+ public void onDismiss(Snackbar snackbar) {
+ ((FabContainer) activity).fabRestorePosition();
+ }
+ });
+ }
+ return bar;
+ }
+
+ public interface Showable {
+ public void show();
+
+ }
+
+ public interface ActionListener {
+ public void onAction();
+
+ }
+
} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java
index 36f38045f..b8d4ea7d2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java
@@ -47,7 +47,7 @@ public class QrCodeUtils {
*/
public static Bitmap getQRCodeBitmap(final String input, final int size) {
try {
- final Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
+ final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
final BitMatrix result = new QRCodeWriter().encode(input, BarcodeFormat.QR_CODE, size,
size, hints);
@@ -59,7 +59,7 @@ public class QrCodeUtils {
for (int y = 0; y < height; y++) {
final int offset = y * width;
for (int x = 0; x < width; x++) {
- pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
+ pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/AspectRatioImageView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/AspectRatioImageView.java
new file mode 100644
index 000000000..0df5ba5e8
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/AspectRatioImageView.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 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.widget.ImageView;
+
+import org.sufficientlysecure.keychain.R;
+
+/**
+ * Maintains an aspect ratio based on either width or height. Disabled by default.
+ *
+ * from https://gist.github.com/JakeWharton/2856179
+ */
+public class AspectRatioImageView extends ImageView {
+ // NOTE: These must be kept in sync with the AspectRatioImageView attributes in attrs.xml.
+ public static final int MEASUREMENT_WIDTH = 0;
+ public static final int MEASUREMENT_HEIGHT = 1;
+
+ private static final float DEFAULT_ASPECT_RATIO = 1f;
+ private static final boolean DEFAULT_ASPECT_RATIO_ENABLED = false;
+ private static final int DEFAULT_DOMINANT_MEASUREMENT = MEASUREMENT_WIDTH;
+
+ private float aspectRatio;
+ private boolean aspectRatioEnabled;
+ private int dominantMeasurement;
+
+ public AspectRatioImageView(Context context) {
+ this(context, null);
+ }
+
+ public AspectRatioImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AspectRatioImageView);
+ aspectRatio = a.getFloat(R.styleable.AspectRatioImageView_aspectRatio, DEFAULT_ASPECT_RATIO);
+ aspectRatioEnabled = a.getBoolean(R.styleable.AspectRatioImageView_aspectRatioEnabled,
+ DEFAULT_ASPECT_RATIO_ENABLED);
+ dominantMeasurement = a.getInt(R.styleable.AspectRatioImageView_dominantMeasurement,
+ DEFAULT_DOMINANT_MEASUREMENT);
+ a.recycle();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (!aspectRatioEnabled) return;
+
+ int newWidth;
+ int newHeight;
+ switch (dominantMeasurement) {
+ case MEASUREMENT_WIDTH:
+ newWidth = getMeasuredWidth();
+ newHeight = (int) (newWidth * aspectRatio);
+ break;
+
+ case MEASUREMENT_HEIGHT:
+ newHeight = getMeasuredHeight();
+ newWidth = (int) (newHeight * aspectRatio);
+ break;
+
+ default:
+ throw new IllegalStateException("Unknown measurement with ID " + dominantMeasurement);
+ }
+
+ setMeasuredDimension(newWidth, newHeight);
+ }
+
+ /**
+ * Get the aspect ratio for this image view.
+ */
+ public float getAspectRatio() {
+ return aspectRatio;
+ }
+
+ /**
+ * Set the aspect ratio for this image view. This will update the view instantly.
+ */
+ public void setAspectRatio(float aspectRatio) {
+ this.aspectRatio = aspectRatio;
+ if (aspectRatioEnabled) {
+ requestLayout();
+ }
+ }
+
+ /**
+ * Get whether or not forcing the aspect ratio is enabled.
+ */
+ public boolean getAspectRatioEnabled() {
+ return aspectRatioEnabled;
+ }
+
+ /**
+ * set whether or not forcing the aspect ratio is enabled. This will re-layout the view.
+ */
+ public void setAspectRatioEnabled(boolean aspectRatioEnabled) {
+ this.aspectRatioEnabled = aspectRatioEnabled;
+ requestLayout();
+ }
+
+ /**
+ * Get the dominant measurement for the aspect ratio.
+ */
+ public int getDominantMeasurement() {
+ return dominantMeasurement;
+ }
+
+ /**
+ * Set the dominant measurement for the aspect ratio.
+ *
+ * @see #MEASUREMENT_WIDTH
+ * @see #MEASUREMENT_HEIGHT
+ */
+ public void setDominantMeasurement(int dominantMeasurement) {
+ if (dominantMeasurement != MEASUREMENT_HEIGHT && dominantMeasurement != MEASUREMENT_WIDTH) {
+ throw new IllegalArgumentException("Invalid measurement type.");
+ }
+ this.dominantMeasurement = dominantMeasurement;
+ requestLayout();
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
index 14f42eb04..6d0e6556f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
@@ -27,9 +27,9 @@ import android.util.AttributeSet;
import android.widget.ImageView;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public class CertifyKeySpinner extends KeySpinner {
@@ -86,32 +86,33 @@ public class CertifyKeySpinner extends KeySpinner {
super.onLoadFinished(loader, data);
if (loader.getId() == LOADER_ID) {
+ mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY);
+ mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
+ mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
+
// If there is only one choice, pick it by default
if (mAdapter.getCount() == 2) {
// preselect if key can certify
- if (data.moveToPosition(1) && !data.isNull(mIndexHasCertify)) {
+ if (data.moveToPosition(0) && !data.isNull(mIndexHasCertify)) {
setSelection(1);
}
}
- mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY);
- mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
- mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
}
}
@Override
boolean setStatus(Context context, Cursor cursor, ImageView statusView) {
if (cursor.getInt(mIndexIsRevoked) != 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, true);
+ KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray);
return false;
}
if (cursor.getInt(mIndexIsExpired) != 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, true);
+ KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray);
return false;
}
// don't invalidate the "None" entry, which is also null!
if (cursor.getPosition() != 0 && cursor.isNull(mIndexHasCertify)) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, true);
+ KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, R.color.bg_gray);
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
index e03a14989..f05f5f96b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
@@ -28,7 +28,6 @@ import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
-import android.text.SpannableStringBuilder;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -42,14 +41,13 @@ import com.tokenautocomplete.TokenCompleteTextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.ContactHelper;
import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.ContactHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -165,7 +163,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
setAdapter(new EncryptKeyAdapter(Collections.<EncryptionKey>emptyList()));
return;
}
- ArrayList<EncryptionKey> keys = new ArrayList<EncryptionKey>();
+ ArrayList<EncryptionKey> keys = new ArrayList<>();
while (cursor.moveToNext()) {
try {
EncryptionKey key = new EncryptionKey(cursor);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
index b456b61ab..34e7b639a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
@@ -33,17 +33,16 @@ 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">
-
- <include layout="@layout/ELEMENTS_TO_BE_FOLDED"/>
-
- </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout>
-
+ * <p/>
+ * <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">
+ * <p/>
+ * <include layout="@layout/ELEMENTS_TO_BE_FOLDED"/>
+ * <p/>
+ * </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout>
*/
public class FoldableLinearLayout extends LinearLayout {
@@ -75,6 +74,7 @@ public class FoldableLinearLayout extends LinearLayout {
/**
* Load given attributes to inner variables,
+ *
* @param context
* @param attrs
*/
@@ -87,8 +87,8 @@ public class FoldableLinearLayout extends LinearLayout {
a.recycle();
}
// If any attribute isn't found then set a default one
- mFoldedLabel = (mFoldedLabel == null) ? context.getString(R.id.none) : mFoldedLabel;
- mUnFoldedLabel = (mUnFoldedLabel == null) ? context.getString(R.id.none) : mUnFoldedLabel;
+ mFoldedLabel = (mFoldedLabel == null) ? context.getString(R.string.none) : mFoldedLabel;
+ mUnFoldedLabel = (mUnFoldedLabel == null) ? context.getString(R.string.none) : mUnFoldedLabel;
}
@Override
@@ -138,7 +138,7 @@ public class FoldableLinearLayout extends LinearLayout {
private void initialiseInnerViews() {
mFoldableIcon = (ImageView) mFoldableLayout.findViewById(R.id.foldableIcon);
- mFoldableIcon.setImageResource(R.drawable.ic_action_expand);
+ mFoldableIcon.setImageResource(R.drawable.ic_expand_more_black_24dp);
mFoldableTextView = (TextView) mFoldableLayout.findViewById(R.id.foldableText);
mFoldableTextView.setText(mFoldedLabel);
@@ -151,7 +151,7 @@ public class FoldableLinearLayout extends LinearLayout {
public void onClick(View view) {
mFolded = !mFolded;
if (mFolded) {
- mFoldableIcon.setImageResource(R.drawable.ic_action_collapse);
+ mFoldableIcon.setImageResource(R.drawable.ic_expand_less_black_24dp);
mFoldableContainer.setVisibility(View.VISIBLE);
AlphaAnimation animation = new AlphaAnimation(0f, 1f);
animation.setDuration(mShortAnimationDuration);
@@ -159,12 +159,13 @@ public class FoldableLinearLayout extends LinearLayout {
mFoldableTextView.setText(mUnFoldedLabel);
} else {
- mFoldableIcon.setImageResource(R.drawable.ic_action_expand);
+ mFoldableIcon.setImageResource(R.drawable.ic_expand_more_black_24dp);
AlphaAnimation animation = new AlphaAnimation(1f, 0f);
animation.setDuration(mShortAnimationDuration);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
- public void onAnimationStart(Animation animation) { }
+ public void onAnimationStart(Animation animation) {
+ }
@Override
public void onAnimationEnd(Animation animation) {
@@ -173,7 +174,8 @@ public class FoldableLinearLayout extends LinearLayout {
}
@Override
- public void onAnimationRepeat(Animation animation) { }
+ public void onAnimationRepeat(Animation animation) {
+ }
});
mFoldableContainer.startAnimation(animation);
mFoldableTextView.setText(mFoldedLabel);
@@ -185,6 +187,7 @@ public class FoldableLinearLayout extends LinearLayout {
/**
* Adds provided child view to foldableContainer View
+ *
* @param child
*/
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
index 7ec6ffe95..c8eceea50 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
@@ -24,24 +24,28 @@ import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
+import android.support.v7.internal.widget.TintSpinner;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
-import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
-public abstract class KeySpinner extends Spinner implements LoaderManager.LoaderCallbacks<Cursor> {
+/**
+ * Use TintSpinner from AppCompat lib instead of Spinner. Fixes white dropdown icon.
+ * Related: http://stackoverflow.com/a/27713090
+ */
+public abstract class KeySpinner extends TintSpinner implements LoaderManager.LoaderCallbacks<Cursor> {
public interface OnKeyChangedListener {
public void onKeyChanged(long masterKeyId);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ListAwareSwipeRefreshLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ListAwareSwipeRefreshLayout.java
deleted file mode 100644
index b3c3eb417..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ListAwareSwipeRefreshLayout.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2014 Daniel Albert
- *
- * 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.support.v4.widget.NoScrollableSwipeRefreshLayout;
-import android.util.AttributeSet;
-import android.view.InputDevice;
-import android.view.InputDevice.MotionRange;
-import android.view.MotionEvent;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.util.Log;
-
-import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
-
-public class ListAwareSwipeRefreshLayout extends NoScrollableSwipeRefreshLayout {
-
- private StickyListHeadersListView mStickyListHeadersListView = null;
- private boolean mIsLocked = false;
-
- /**
- * Constructors
- */
- public ListAwareSwipeRefreshLayout(Context context) {
- super(context);
- }
-
- public ListAwareSwipeRefreshLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /**
- * Getters / Setters
- */
- public void setStickyListHeadersListView(StickyListHeadersListView stickyListHeadersListView) {
- mStickyListHeadersListView = stickyListHeadersListView;
- }
-
- public StickyListHeadersListView getStickyListHeadersListView() {
- return mStickyListHeadersListView;
- }
-
- public void setIsLocked(boolean locked) {
- mIsLocked = locked;
- }
-
- public boolean getIsLocked() {
- return mIsLocked;
- }
-
- @Override
- public boolean canChildScrollUp() {
- if (mStickyListHeadersListView == null) {
- return super.canChildScrollUp();
- }
-
- return (mIsLocked || (
- mStickyListHeadersListView.getWrappedList().getChildCount() > 0
- && (mStickyListHeadersListView.getWrappedList().getChildAt(0).getTop() < 0
- || mStickyListHeadersListView.getFirstVisiblePosition() > 0)
- )
- );
- }
-
- /** Called on a touch event, this method exempts a small area in the upper right from pull to
- * refresh handling.
- *
- * If the touch event happens somewhere in the upper right corner of the screen, we return false
- * to indicate that the event was not handled. This ensures events in that area are always
- * handed through to the list scrollbar handle. For all other cases, we pass the message through
- * to the pull to refresh handler.
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // The device may be null. This actually happens
- if (event.getDevice() != null) {
- // MotionEvent.AXIS_X is api level 12, for some reason, so we use a constant 0 here
- float ratioX = event.getX() / event.getDevice().getMotionRange(0).getMax();
- float ratioY = event.getY() / event.getDevice().getMotionRange(1).getMax();
- // if this is the upper right corner, don't handle as pull to refresh event
- if (ratioX > 0.85f && ratioY < 0.15f) {
- return false;
- }
- }
- return super.onTouchEvent(event);
- }
-
-} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
index 59d05a62e..fe91e306e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
@@ -84,15 +84,15 @@ public class SignKeySpinner extends KeySpinner {
@Override
boolean setStatus(Context context, Cursor cursor, ImageView statusView) {
if (cursor.getInt(mIndexIsRevoked) != 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, true);
+ KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray);
return false;
}
if (cursor.getInt(mIndexIsExpired) != 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, true);
+ KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_EXPIRED, R.color.bg_gray);
return false;
}
if (cursor.getInt(mIndexHasSign) == 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, true);
+ KeyFormattingUtils.setStatusImage(getContext(), statusView, null, KeyFormattingUtils.STATE_UNAVAILABLE, R.color.bg_gray);
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java
deleted file mode 100644
index 17471c86c..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabLayout.java
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * 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.ui.widget;
-
-import android.content.Context;
-import android.graphics.Typeface;
-import android.os.Build;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.HorizontalScrollView;
-import android.widget.TextView;
-
-/**
- * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html
- */
-
-/**
- * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
- * the user's scroll progress.
- * <p/>
- * To use the component, simply add it to your view hierarchy. Then in your
- * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
- * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
- * <p/>
- * The colors can be customized in two ways. The first and simplest is to provide an array of colors
- * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
- * alternative is via the {@link TabColorizer} interface which provides you complete control over
- * which color is used for any individual position.
- * <p/>
- * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
- * providing the layout ID of your custom layout.
- */
-public class SlidingTabLayout extends HorizontalScrollView {
-
- /**
- * Allows complete control over the colors drawn in the tab layout. Set with
- * {@link #setCustomTabColorizer(TabColorizer)}.
- */
- public interface TabColorizer {
-
- /**
- * @return return the color of the indicator used when {@code position} is selected.
- */
- int getIndicatorColor(int position);
-
- /**
- * @return return the color of the divider drawn to the right of {@code position}.
- */
- int getDividerColor(int position);
-
- }
-
- private static final int TITLE_OFFSET_DIPS = 24;
- private static final int TAB_VIEW_PADDING_DIPS = 16;
- private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
-
- private int mTitleOffset;
-
- private int mTabViewLayoutId;
- private int mTabViewTextViewId;
-
- private ViewPager mViewPager;
- private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
-
- private final SlidingTabStrip mTabStrip;
-
- public SlidingTabLayout(Context context) {
- this(context, null);
- }
-
- public SlidingTabLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- // Disable the Scroll Bar
- setHorizontalScrollBarEnabled(false);
- // Make sure that the Tab Strips fills this View
- setFillViewport(true);
-
- mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
-
- mTabStrip = new SlidingTabStrip(context);
- addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- }
-
- /**
- * Set the custom {@link TabColorizer} to be used.
- * <p/>
- * If you only require simple custmisation then you can use
- * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
- * similar effects.
- */
- public void setCustomTabColorizer(TabColorizer tabColorizer) {
- mTabStrip.setCustomTabColorizer(tabColorizer);
- }
-
- /**
- * Sets the colors to be used for indicating the selected tab. These colors are treated as a
- * circular array. Providing one color will mean that all tabs are indicated with the same color.
- */
- public void setSelectedIndicatorColors(int... colors) {
- mTabStrip.setSelectedIndicatorColors(colors);
- }
-
- /**
- * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
- * Providing one color will mean that all tabs are indicated with the same color.
- */
- public void setDividerColors(int... colors) {
- mTabStrip.setDividerColors(colors);
- }
-
- /**
- * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
- * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
- * that the layout can update it's scroll position correctly.
- *
- * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
- */
- public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
- mViewPagerPageChangeListener = listener;
- }
-
- /**
- * Set the custom layout to be inflated for the tab views.
- *
- * @param layoutResId Layout id to be inflated
- * @param textViewId id of the {@link TextView} in the inflated view
- */
- public void setCustomTabView(int layoutResId, int textViewId) {
- mTabViewLayoutId = layoutResId;
- mTabViewTextViewId = textViewId;
- }
-
- /**
- * Sets the associated view pager. Note that the assumption here is that the pager content
- * (number of tabs and tab titles) does not change after this call has been made.
- */
- public void setViewPager(ViewPager viewPager) {
- mTabStrip.removeAllViews();
-
- mViewPager = viewPager;
- if (viewPager != null) {
- viewPager.setOnPageChangeListener(new InternalViewPagerListener());
- populateTabStrip();
- }
- }
-
- /**
- * Create a default view to be used for tabs. This is called if a custom tab view is not set via
- * {@link #setCustomTabView(int, int)}.
- */
- protected TextView createDefaultTabView(Context context) {
- TextView textView = new TextView(context);
- textView.setGravity(Gravity.CENTER);
- textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
- textView.setTypeface(Typeface.DEFAULT_BOLD);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- // If we're running on Honeycomb or newer, then we can use the Theme's
- // selectableItemBackground to ensure that the View has a pressed state
- TypedValue outValue = new TypedValue();
- getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
- outValue, true);
- textView.setBackgroundResource(outValue.resourceId);
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
- textView.setAllCaps(true);
- }
-
- int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
- textView.setPadding(padding, padding, padding, padding);
-
- return textView;
- }
-
- private void populateTabStrip() {
- final PagerAdapter adapter = mViewPager.getAdapter();
- final View.OnClickListener tabClickListener = new TabClickListener();
-
- for (int i = 0; i < adapter.getCount(); i++) {
- View tabView = null;
- TextView tabTitleView = null;
-
- if (mTabViewLayoutId != 0) {
- // If there is a custom tab view layout id set, try and inflate it
- tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
- false);
- tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
- }
-
- if (tabView == null) {
- tabView = createDefaultTabView(getContext());
- }
-
- if (tabTitleView == null && TextView.class.isInstance(tabView)) {
- tabTitleView = (TextView) tabView;
- }
-
- tabTitleView.setText(adapter.getPageTitle(i));
- tabView.setOnClickListener(tabClickListener);
-
- mTabStrip.addView(tabView);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (mViewPager != null) {
- scrollToTab(mViewPager.getCurrentItem(), 0);
- }
- }
-
- private void scrollToTab(int tabIndex, int positionOffset) {
- final int tabStripChildCount = mTabStrip.getChildCount();
- if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
- return;
- }
-
- View selectedChild = mTabStrip.getChildAt(tabIndex);
- if (selectedChild != null) {
- int targetScrollX = selectedChild.getLeft() + positionOffset;
-
- if (tabIndex > 0 || positionOffset > 0) {
- // If we're not at the first child and are mid-scroll, make sure we obey the offset
- targetScrollX -= mTitleOffset;
- }
-
- scrollTo(targetScrollX, 0);
- }
- }
-
- private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
- private int mScrollState;
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- int tabStripChildCount = mTabStrip.getChildCount();
- if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
- return;
- }
-
- mTabStrip.onViewPagerPageChanged(position, positionOffset);
-
- View selectedTitle = mTabStrip.getChildAt(position);
- int extraOffset = (selectedTitle != null)
- ? (int) (positionOffset * selectedTitle.getWidth())
- : 0;
- scrollToTab(position, extraOffset);
-
- if (mViewPagerPageChangeListener != null) {
- mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
- positionOffsetPixels);
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- mScrollState = state;
-
- if (mViewPagerPageChangeListener != null) {
- mViewPagerPageChangeListener.onPageScrollStateChanged(state);
- }
- }
-
- @Override
- public void onPageSelected(int position) {
- if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
- mTabStrip.onViewPagerPageChanged(position, 0f);
- scrollToTab(position, 0);
- }
-
- if (mViewPagerPageChangeListener != null) {
- mViewPagerPageChangeListener.onPageSelected(position);
- }
- }
-
- }
-
- private class TabClickListener implements View.OnClickListener {
- @Override
- public void onClick(View v) {
- for (int i = 0; i < mTabStrip.getChildCount(); i++) {
- if (v == mTabStrip.getChildAt(i)) {
- mViewPager.setCurrentItem(i);
- return;
- }
- }
- }
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java
deleted file mode 100644
index 4c41e12c5..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SlidingTabStrip.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * 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.ui.widget;
-
-import android.R;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.LinearLayout;
-
-/**
- * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html
- */
-class SlidingTabStrip extends LinearLayout {
-
- private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
- private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
- private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
- private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFFAA66CC;
-
- private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
- private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
- private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
-
- private final int mBottomBorderThickness;
- private final Paint mBottomBorderPaint;
-
- private final int mSelectedIndicatorThickness;
- private final Paint mSelectedIndicatorPaint;
-
- private final int mDefaultBottomBorderColor;
-
- private final Paint mDividerPaint;
- private final float mDividerHeight;
-
- private int mSelectedPosition;
- private float mSelectionOffset;
-
- private SlidingTabLayout.TabColorizer mCustomTabColorizer;
- private final SimpleTabColorizer mDefaultTabColorizer;
-
- SlidingTabStrip(Context context) {
- this(context, null);
- }
-
- SlidingTabStrip(Context context, AttributeSet attrs) {
- super(context, attrs);
- setWillNotDraw(false);
-
- final float density = getResources().getDisplayMetrics().density;
-
- TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
- final int themeForegroundColor = outValue.data;
-
- mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
- DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
-
- mDefaultTabColorizer = new SimpleTabColorizer();
- mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
- mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
- DEFAULT_DIVIDER_COLOR_ALPHA));
-
- mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
- mBottomBorderPaint = new Paint();
- mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
-
- mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
- mSelectedIndicatorPaint = new Paint();
-
- mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
- mDividerPaint = new Paint();
- mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
- }
-
- void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
- mCustomTabColorizer = customTabColorizer;
- invalidate();
- }
-
- void setSelectedIndicatorColors(int... colors) {
- // Make sure that the custom colorizer is removed
- mCustomTabColorizer = null;
- mDefaultTabColorizer.setIndicatorColors(colors);
- invalidate();
- }
-
- void setDividerColors(int... colors) {
- // Make sure that the custom colorizer is removed
- mCustomTabColorizer = null;
- mDefaultTabColorizer.setDividerColors(colors);
- invalidate();
- }
-
- void onViewPagerPageChanged(int position, float positionOffset) {
- mSelectedPosition = position;
- mSelectionOffset = positionOffset;
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- final int height = getHeight();
- final int childCount = getChildCount();
- final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
- final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
- ? mCustomTabColorizer
- : mDefaultTabColorizer;
-
- // Thick colored underline below the current selection
- if (childCount > 0) {
- View selectedTitle = getChildAt(mSelectedPosition);
- int left = selectedTitle.getLeft();
- int right = selectedTitle.getRight();
- int color = tabColorizer.getIndicatorColor(mSelectedPosition);
-
- if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
- int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
- if (color != nextColor) {
- color = blendColors(nextColor, color, mSelectionOffset);
- }
-
- // Draw the selection partway between the tabs
- View nextTitle = getChildAt(mSelectedPosition + 1);
- left = (int) (mSelectionOffset * nextTitle.getLeft() +
- (1.0f - mSelectionOffset) * left);
- right = (int) (mSelectionOffset * nextTitle.getRight() +
- (1.0f - mSelectionOffset) * right);
- }
-
- mSelectedIndicatorPaint.setColor(color);
-
- canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
- height, mSelectedIndicatorPaint);
- }
-
- // Thin underline along the entire bottom edge
- canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
-
- // Vertical separators between the titles
- int separatorTop = (height - dividerHeightPx) / 2;
- for (int i = 0; i < childCount - 1; i++) {
- View child = getChildAt(i);
- mDividerPaint.setColor(tabColorizer.getDividerColor(i));
- canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
- separatorTop + dividerHeightPx, mDividerPaint);
- }
- }
-
- /**
- * Set the alpha value of the {@code color} to be the given {@code alpha} value.
- */
- private static int setColorAlpha(int color, byte alpha) {
- return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
- }
-
- /**
- * Blend {@code color1} and {@code color2} using the given ratio.
- *
- * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
- * 0.0 will return {@code color2}.
- */
- private static int blendColors(int color1, int color2, float ratio) {
- final float inverseRation = 1f - ratio;
- float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
- float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
- float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
- return Color.rgb((int) r, (int) g, (int) b);
- }
-
- private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
- private int[] mIndicatorColors;
- private int[] mDividerColors;
-
- @Override
- public final int getIndicatorColor(int position) {
- return mIndicatorColors[position % mIndicatorColors.length];
- }
-
- @Override
- public final int getDividerColor(int position) {
- return mDividerColors[position % mDividerColors.length];
- }
-
- void setIndicatorColors(int... colors) {
- mIndicatorColors = colors;
- }
-
- void setDividerColors(int... colors) {
- mDividerColors = colors;
- }
- }
-} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
index 99db634ac..c1955f75b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
@@ -23,7 +23,6 @@ import android.app.Activity;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import java.util.HashMap;
@@ -32,9 +31,9 @@ import java.util.HashMap;
public class AlgorithmNames {
Activity mActivity;
- HashMap<Integer, String> mEncryptionNames = new HashMap<Integer, String>();
- HashMap<Integer, String> mHashNames = new HashMap<Integer, String>();
- HashMap<Integer, String> mCompressionNames = new HashMap<Integer, String>();
+ HashMap<Integer, String> mEncryptionNames = new HashMap<>();
+ HashMap<Integer, String> mHashNames = new HashMap<>();
+ HashMap<Integer, String> mCompressionNames = new HashMap<>();
public AlgorithmNames(Activity context) {
super();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
index 4ce7a1bac..60bc846b2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
@@ -35,9 +35,9 @@ import android.util.Patterns;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
import java.io.InputStream;
import java.util.ArrayList;
@@ -73,20 +73,20 @@ public class ContactHelper {
ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?";
public static final String ID_SELECTION = ContactsContract.RawContacts._ID + "=?";
- private static final Map<String, Bitmap> photoCache = new HashMap<String, Bitmap>();
+ private static final Map<String, Bitmap> photoCache = new HashMap<>();
public static List<String> getPossibleUserEmails(Context context) {
Set<String> accountMails = getAccountEmails(context);
accountMails.addAll(getMainProfileContactEmails(context));
// now return the Set (without duplicates) as a List
- return new ArrayList<String>(accountMails);
+ return new ArrayList<>(accountMails);
}
public static List<String> getPossibleUserNames(Context context) {
Set<String> accountMails = getAccountEmails(context);
Set<String> names = getContactNamesFromEmails(context, accountMails);
names.addAll(getMainProfileContactName(context));
- return new ArrayList<String>(names);
+ return new ArrayList<>(names);
}
/**
@@ -97,7 +97,7 @@ public class ContactHelper {
*/
private static Set<String> getAccountEmails(Context context) {
final Account[] accounts = AccountManager.get(context).getAccounts();
- final Set<String> emailSet = new HashSet<String>();
+ final Set<String> emailSet = new HashSet<>();
for (Account account : accounts) {
if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
emailSet.add(account.name);
@@ -116,7 +116,7 @@ public class ContactHelper {
*/
private static Set<String> getContactNamesFromEmails(Context context, Set<String> emails) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- Set<String> names = new HashSet<String>();
+ Set<String> names = new HashSet<>();
for (String email : emails) {
ContentResolver resolver = context.getContentResolver();
Cursor profileCursor = resolver.query(
@@ -128,7 +128,7 @@ public class ContactHelper {
);
if (profileCursor == null) return null;
- Set<String> currNames = new HashSet<String>();
+ Set<String> currNames = new HashSet<>();
while (profileCursor.moveToNext()) {
String name = profileCursor.getString(1);
if (name != null) {
@@ -140,7 +140,7 @@ public class ContactHelper {
}
return names;
} else {
- return new HashSet<String>();
+ return new HashSet<>();
}
}
@@ -172,7 +172,7 @@ public class ContactHelper {
);
if (profileCursor == null) return null;
- Set<String> emails = new HashSet<String>();
+ Set<String> emails = new HashSet<>();
while (profileCursor.moveToNext()) {
String email = profileCursor.getString(0);
if (email != null) {
@@ -182,7 +182,7 @@ public class ContactHelper {
profileCursor.close();
return emails;
} else {
- return new HashSet<String>();
+ return new HashSet<>();
}
}
@@ -201,7 +201,7 @@ public class ContactHelper {
null, null, null);
if (profileCursor == null) return null;
- Set<String> names = new HashSet<String>();
+ Set<String> names = new HashSet<>();
// should only contain one entry!
while (profileCursor.moveToNext()) {
String name = profileCursor.getString(0);
@@ -210,9 +210,9 @@ public class ContactHelper {
}
}
profileCursor.close();
- return new ArrayList<String>(names);
+ return new ArrayList<>(names);
} else {
- return new ArrayList<String>();
+ return new ArrayList<>();
}
}
@@ -221,9 +221,9 @@ public class ContactHelper {
Cursor mailCursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
new String[]{ContactsContract.CommonDataKinds.Email.DATA},
null, null, null);
- if (mailCursor == null) return new ArrayList<String>();
+ if (mailCursor == null) return new ArrayList<>();
- Set<String> mails = new HashSet<String>();
+ Set<String> mails = new HashSet<>();
while (mailCursor.moveToNext()) {
String email = mailCursor.getString(0);
if (email != null) {
@@ -231,7 +231,7 @@ public class ContactHelper {
}
}
mailCursor.close();
- return new ArrayList<String>(mails);
+ return new ArrayList<>(mails);
}
public static List<String> getContactNames(Context context) {
@@ -239,9 +239,9 @@ public class ContactHelper {
Cursor cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI,
new String[]{ContactsContract.Contacts.DISPLAY_NAME},
null, null, null);
- if (cursor == null) return new ArrayList<String>();
+ if (cursor == null) return new ArrayList<>();
- Set<String> names = new HashSet<String>();
+ Set<String> names = new HashSet<>();
while (cursor.moveToNext()) {
String name = cursor.getString(0);
if (name != null) {
@@ -249,7 +249,7 @@ public class ContactHelper {
}
}
cursor.close();
- return new ArrayList<String>(names);
+ return new ArrayList<>(names);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@@ -309,7 +309,7 @@ public class ContactHelper {
boolean isExpired = !cursor.isNull(4) && new Date(cursor.getLong(4) * 1000).before(new Date());
boolean isRevoked = cursor.getInt(5) > 0;
int rawContactId = findRawContactId(resolver, fingerprint);
- ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+ ArrayList<ContentProviderOperation> ops = new ArrayList<>();
// Do not store expired or revoked keys in contact db - and remove them if they already exist
if (isExpired || isRevoked) {
@@ -351,7 +351,7 @@ public class ContactHelper {
* @return a set of all key fingerprints currently present in the contact db
*/
private static Set<String> getRawContactFingerprints(ContentResolver resolver) {
- HashSet<String> result = new HashSet<String>();
+ HashSet<String> result = new HashSet<>();
Cursor fingerprints = resolver.query(ContactsContract.RawContacts.CONTENT_URI, SOURCE_ID_PROJECTION,
ACCOUNT_TYPE_SELECTION, new String[]{Constants.ACCOUNT_TYPE}, null);
if (fingerprints != null) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
index 49d4d8bf8..8334b37ec 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
@@ -42,13 +42,13 @@ public class EmailKeyHelper {
public static void importAll(Context context, Messenger messenger, List<String> mails) {
// Collect all candidates as ImportKeysListEntry (set for deduplication)
- Set<ImportKeysListEntry> entries = new HashSet<ImportKeysListEntry>();
+ Set<ImportKeysListEntry> entries = new HashSet<>();
for (String mail : mails) {
entries.addAll(getEmailKeys(context, mail));
}
// Put them in a list and import
- ArrayList<ParcelableKeyRing> keys = new ArrayList<ParcelableKeyRing>(entries.size());
+ ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
for (ImportKeysListEntry entry : entries) {
keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
}
@@ -56,7 +56,7 @@ public class EmailKeyHelper {
}
public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail) {
- Set<ImportKeysListEntry> keys = new HashSet<ImportKeysListEntry>();
+ Set<ImportKeysListEntry> keys = new HashSet<>();
// Try _hkp._tcp SRV record first
String[] mailparts = mail.split("@");
@@ -90,7 +90,7 @@ public class EmailKeyHelper {
}
public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer) {
- Set<ImportKeysListEntry> keys = new HashSet<ImportKeysListEntry>();
+ Set<ImportKeysListEntry> keys = new HashSet<>();
try {
for (ImportKeysListEntry key : keyServer.search(mail)) {
if (key.isRevoked() || key.isExpired()) continue;
@@ -103,6 +103,6 @@ public class EmailKeyHelper {
} catch (Keyserver.QueryFailedException ignored) {
} catch (Keyserver.QueryNeedsRepairException ignored) {
}
- return new ArrayList<ImportKeysListEntry>(keys);
+ return new ArrayList<>(keys);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
index 7492d95b2..1d78ed80e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
@@ -24,13 +24,11 @@ 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 android.support.v4.app.FragmentActivity;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
@@ -42,9 +40,9 @@ import java.io.File;
public class ExportHelper {
protected File mExportFile;
- ActionBarActivity mActivity;
+ FragmentActivity mActivity;
- public ExportHelper(ActionBarActivity activity) {
+ public ExportHelper(FragmentActivity activity) {
super();
this.mActivity = activity;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FabContainer.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FabContainer.java
new file mode 100644
index 000000000..116b98d1e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FabContainer.java
@@ -0,0 +1,8 @@
+package org.sufficientlysecure.keychain.util;
+
+public interface FabContainer {
+
+ void fabMoveUp(int height);
+ void fabRestorePosition();
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
index f89ffd139..1f73dcb28 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
@@ -25,16 +25,28 @@ import java.io.InputStream;
public class InputData {
private PositionAwareInputStream mInputStream;
private long mSize;
+ String mOriginalFilename;
+
+ public InputData(InputStream inputStream, long size, String originalFilename) {
+ mInputStream = new PositionAwareInputStream(inputStream);
+ mSize = size;
+ mOriginalFilename = originalFilename;
+ }
public InputData(InputStream inputStream, long size) {
mInputStream = new PositionAwareInputStream(inputStream);
mSize = size;
+ mOriginalFilename = "";
}
public InputStream getInputStream() {
return mInputStream;
}
+ public String getOriginalFilename () {
+ return mOriginalFilename;
+ }
+
public long getSize() {
return mSize;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Iso7816TLV.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Iso7816TLV.java
index 90afd3bc0..c0483ad04 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Iso7816TLV.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Iso7816TLV.java
@@ -125,7 +125,7 @@ public class Iso7816TLV {
public static Iso7816TLV[] readList(byte[] data, boolean recursive) throws IOException {
ByteBuffer buf = ByteBuffer.wrap(data);
- ArrayList<Iso7816TLV> result = new ArrayList<Iso7816TLV>();
+ ArrayList<Iso7816TLV> result = new ArrayList<>();
// read while data is available. this will fail if there is trailing data!
while (buf.hasRemaining()) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java
index 76ec9f75f..943b913d7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java
@@ -17,21 +17,6 @@
package org.sufficientlysecure.keychain.util;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Messenger;
-
-import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
-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 java.util.ArrayList;
-import java.util.List;
-
public class KeyUpdateHelper {
/*
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java
index b4f7c5767..8b165cd57 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Log.java
@@ -21,9 +21,6 @@ import android.os.Bundle;
import org.sufficientlysecure.keychain.Constants;
-import java.io.IOException;
-import java.io.StreamTokenizer;
-import java.io.StringReader;
import java.util.Iterator;
import java.util.Set;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java
index 3081021cf..6f9cb277e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java
@@ -32,9 +32,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
/**
* When sending large data (over 1MB) through Androids Binder IPC you get
@@ -106,7 +104,6 @@ public class ParcelableFileCache<E extends Parcelable> {
throw new IOException(e);
}
- // yes this is sloppy data flow. WE WOULDN'T NEED THIS WITH TUPLE RETURN TYPES
final int numEntries = ois.readInt();
return new IteratorWithSize<E>() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
index 65f4af880..a36af5c87 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
@@ -172,7 +172,7 @@ public class Preferences {
}
public boolean useDefaultYubikeyPin() {
- return mSharedPreferences.getBoolean(Pref.USE_DEFAULT_YUBIKEY_PIN, true);
+ return mSharedPreferences.getBoolean(Pref.USE_DEFAULT_YUBIKEY_PIN, false);
}
public void setUseDefaultYubikeyPin(boolean useDefaultYubikeyPin) {
@@ -182,7 +182,7 @@ public class Preferences {
}
public boolean useNumKeypadForYubikeyPin() {
- return mSharedPreferences.getBoolean(Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN, false);
+ return mSharedPreferences.getBoolean(Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN, true);
}
public void setUseNumKeypadForYubikeyPin(boolean useNumKeypadForYubikeyPin) {
@@ -200,7 +200,7 @@ public class Preferences {
public String[] getKeyServers() {
String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS,
Constants.Defaults.KEY_SERVERS);
- Vector<String> servers = new Vector<String>();
+ Vector<String> servers = new Vector<>();
String chunks[] = rawData.split(",");
for (String c : chunks) {
String tmp = c.trim();
@@ -281,7 +281,7 @@ public class Preferences {
case 3: {
// migrate keyserver to hkps
String[] serversArray = getKeyServers();
- ArrayList<String> servers = new ArrayList<String>(Arrays.asList(serversArray));
+ ArrayList<String> servers = new ArrayList<>(Arrays.asList(serversArray));
ListIterator<String> it = servers.listIterator();
while (it.hasNext()) {
String server = it.next();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java
index 51e58565f..120b84a3b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java
@@ -43,16 +43,18 @@ public class ShareHelper {
* Put together from some stackoverflow posts...
*/
public Intent createChooserExcluding(Intent prototype, String title, String[] activityBlacklist) {
- // Produced an empty list on Huawei U8860 with Android Version 4.0.3 and weird results on 2.3
+ // Produced an empty list on Huawei U8860 with Android Version 4.0.3
// TODO: test on 4.1, 4.2, 4.3, only tested on 4.4
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ // Disabled on 5.0 because using EXTRA_INITIAL_INTENTS prevents the usage based sorting
+ // introduced in 5.0: https://medium.com/@xXxXxXxXxXam/how-lollipops-share-menu-is-organized-d204888f606d
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return Intent.createChooser(prototype, title);
}
- List<LabeledIntent> targetedShareIntents = new ArrayList<LabeledIntent>();
+ List<LabeledIntent> targetedShareIntents = new ArrayList<>();
List<ResolveInfo> resInfoList = mContext.getPackageManager().queryIntentActivities(prototype, 0);
- List<ResolveInfo> resInfoListFiltered = new ArrayList<ResolveInfo>();
+ List<ResolveInfo> resInfoListFiltered = new ArrayList<>();
if (!resInfoList.isEmpty()) {
for (ResolveInfo resolveInfo : resInfoList) {
// do not add blacklisted ones
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java
index 9946d81aa..4ff14e3bb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java
@@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.util;
import android.content.res.AssetManager;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -50,7 +49,7 @@ public class TlsHelper {
}
}
- private static Map<String, byte[]> sStaticCA = new HashMap<String, byte[]>();
+ private static Map<String, byte[]> sStaticCA = new HashMap<>();
public static void addStaticCA(String domain, byte[] certificate) {
sStaticCA.put(domain, certificate);
@@ -120,13 +119,7 @@ public class TlsHelper {
urlConnection.setSSLSocketFactory(context.getSocketFactory());
return urlConnection;
- } catch (CertificateException e) {
- throw new TlsHelperException(e);
- } catch (NoSuchAlgorithmException e) {
- throw new TlsHelperException(e);
- } catch (KeyStoreException e) {
- throw new TlsHelperException(e);
- } catch (KeyManagementException e) {
+ } catch (CertificateException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException e) {
throw new TlsHelperException(e);
}
}