aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure
diff options
context:
space:
mode:
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.java21
-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.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java131
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java85
-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/EditKeyResult.java20
-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.java200
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java63
-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.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java353
-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.java470
-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.java347
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java132
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java37
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java123
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java156
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java265
-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.java678
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java3
-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.java146
-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)67
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java184
-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.java24
-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.java20
-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.java49
-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.java18
-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.java184
-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/KeyListActivity.java222
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java371
-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.java50
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java50
-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.java115
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java575
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java10
-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.java528
-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)74
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvMainFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java)15
-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)13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java125
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java232
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java8
-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.java26
-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/UserIdsAdapter.java50
-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.java72
-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/FileDialogFragment.java2
-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.java113
-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.java5
-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.java49
-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/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
159 files changed, 8409 insertions, 4849 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 854a90713..79065604a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
@@ -21,10 +21,11 @@ import android.content.Context;
import android.os.Parcel;
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;
@@ -88,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();
@@ -102,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;
@@ -264,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<>();
}
/**
@@ -287,14 +288,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
// if there was no user id flagged as primary, use the first one
if (mPrimaryUserId == null) {
- mPrimaryUserId = mUserIds.get(0);
+ mPrimaryUserId = context.getString(R.string.user_id_none);
}
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();
@@ -303,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);
@@ -314,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 3bd412c36..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;
@@ -50,9 +50,6 @@ public class CertifyOperation extends BaseOperation {
CanonicalizedSecretKey certificationKey;
try {
- // certification is always with the master key id, so use that one
- String passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId);
-
log.add(LogType.MSG_CRT_MASTER_FETCH, 1);
CanonicalizedSecretKeyRing secretKeyRing =
mProviderHelper.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId);
@@ -62,6 +59,10 @@ public class CertifyOperation extends BaseOperation {
log.add(LogType.MSG_CRT_ERROR_DIVERT, 2);
return new CertifyResult(CertifyResult.RESULT_ERROR, log);
}
+
+ // certification is always with the master key id, so use that one
+ String passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId);
+
if (!certificationKey.unlock(passphrase)) {
log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);
return new CertifyResult(CertifyResult.RESULT_ERROR, log);
@@ -77,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);
@@ -94,6 +95,12 @@ public class CertifyOperation extends BaseOperation {
try {
+ if (action.mMasterKeyId == parcel.mMasterKeyId) {
+ log.add(LogType.MSG_CRT_ERROR_SELF, 2);
+ certifyError += 1;
+ continue;
+ }
+
if (action.mUserIds == null) {
log.add(LogType.MSG_CRT_CERTIFY_ALL, 2,
KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
new file mode 100644
index 000000000..7f14b08d9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
@@ -0,0 +1,131 @@
+package org.sufficientlysecure.keychain.operations;
+
+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.ContactSyncAdapterService;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** An operation which implements a high level key edit operation.
+ *
+ * This operation provides a higher level interface to the edit and
+ * create key operations in PgpKeyOperation. It takes care of fetching
+ * and saving the key before and after the operation.
+ *
+ * @see SaveKeyringParcel
+ *
+ */
+public class EditKeyOperation extends BaseOperation {
+
+ public EditKeyOperation(Context context, ProviderHelper providerHelper,
+ Progressable progressable, AtomicBoolean cancelled) {
+ super(context, providerHelper, progressable, cancelled);
+ }
+
+ public EditKeyResult execute(SaveKeyringParcel saveParcel, String passphrase) {
+
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_ED, 0);
+
+ if (saveParcel == null) {
+ log.add(LogType.MSG_ED_ERROR_NO_PARCEL, 1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // Perform actual modification (or creation)
+ PgpEditKeyResult modifyResult;
+ {
+ PgpKeyOperation keyOperations =
+ new PgpKeyOperation(new ProgressScaler(mProgressable, 10, 60, 100), mCancelled);
+
+ // If a key id is specified, fetch and edit
+ if (saveParcel.mMasterKeyId != null) {
+ try {
+
+ log.add(LogType.MSG_ED_FETCHING, 1,
+ KeyFormattingUtils.convertKeyIdToHex(saveParcel.mMasterKeyId));
+ CanonicalizedSecretKeyRing secRing =
+ mProviderHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId);
+
+ modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase);
+
+ } catch (NotFoundException e) {
+ log.add(LogType.MSG_ED_ERROR_KEY_NOT_FOUND, 2);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+ } else {
+ // otherwise, create new one
+ modifyResult = keyOperations.createSecretKeyRing(saveParcel);
+ }
+ }
+
+ // Add the result to the log
+ log.add(modifyResult, 1);
+
+ // Check if the action was cancelled
+ if (checkCancelled()) {
+ log.add(LogType.MSG_OPERATION_CANCELLED, 0);
+ return new EditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
+ }
+
+ // If the edit operation didn't succeed, exit here
+ if (!modifyResult.success()) {
+ // error is already logged by modification
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // Cannot cancel from here on out!
+ mProgressable.setPreventCancel();
+
+ // It's a success, so this must be non-null now
+ UncachedKeyRing ring = modifyResult.getRing();
+
+ // Save the new keyring.
+ SaveKeyringResult saveResult = mProviderHelper
+ .saveSecretKeyRing(ring, new ProgressScaler(mProgressable, 60, 95, 100));
+ log.add(saveResult, 1);
+
+ // If the save operation didn't succeed, exit here
+ if (!saveResult.success()) {
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // There is a new passphrase - cache it
+ if (saveParcel.mNewUnlock != null) {
+ log.add(LogType.MSG_ED_CACHING_NEW, 1);
+ PassphraseCacheService.addCachedPassphrase(mContext,
+ ring.getMasterKeyId(),
+ ring.getMasterKeyId(),
+ saveParcel.mNewUnlock.mNewPassphrase != null
+ ? saveParcel.mNewUnlock.mNewPassphrase
+ : saveParcel.mNewUnlock.mNewPin,
+ ring.getPublicKey().getPrimaryUserIdWithFallback());
+ }
+
+ updateProgress(R.string.progress_done, 100, 100);
+
+ // make sure new data is synced into contacts
+ ContactSyncAdapterService.requestSync();
+
+ log.add(LogType.MSG_ED_SUCCESS, 0);
+ return new EditKeyResult(EditKeyResult.RESULT_OK, log, ring.getMasterKeyId());
+
+ }
+
+}
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 da532d2dc..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,13 @@ 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;
import org.sufficientlysecure.keychain.pgp.Progressable;
@@ -39,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;
@@ -57,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
@@ -122,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);
@@ -130,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;
@@ -292,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) {
@@ -399,7 +442,7 @@ public class ImportExportOperation extends BaseOperation {
}
- private ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret,
+ ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret,
OutputStream outStream) {
/* TODO isn't this checked above, with the isStorageMounted call?
@@ -469,12 +512,16 @@ public class ImportExportOperation extends BaseOperation {
log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId));
- { // export public key part
- byte[] data = cursor.getBlob(1);
- arOutStream.write(data);
+ byte[] data = cursor.getBlob(1);
+ CanonicalizedKeyRing ring =
+ UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
+ ring.encode(arOutStream);
- okPublic += 1;
- }
+ okPublic += 1;
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
+ updateProgress(progress++, numKeys);
+ continue;
} finally {
// make sure this is closed
if (arOutStream != null) {
@@ -491,12 +538,18 @@ public class ImportExportOperation extends BaseOperation {
arOutStream.setHeader("Version", version);
}
- // export secret key part
+ // export secret key part
log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId(keyId));
byte[] data = cursor.getBlob(2);
- arOutStream.write(data);
+ CanonicalizedKeyRing ring =
+ UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
+ ring.encode(arOutStream);
okSecret += 1;
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
+ updateProgress(progress++, numKeys);
+ continue;
} finally {
// make sure this is closed
if (arOutStream != null) {
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/EditKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java
index f2acfef1e..abcf575af 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java
@@ -20,34 +20,24 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
-
public class EditKeyResult extends OperationResult {
- private transient UncachedKeyRing mRing;
- public final long mRingMasterKeyId;
+ public final Long mMasterKeyId;
- public EditKeyResult(int result, OperationLog log,
- UncachedKeyRing ring) {
+ public EditKeyResult(int result, OperationLog log, Long masterKeyId) {
super(result, log);
- mRing = ring;
- mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none;
- }
-
- public UncachedKeyRing getRing() {
- return mRing;
+ mMasterKeyId = masterKeyId;
}
public EditKeyResult(Parcel source) {
super(source);
- mRingMasterKeyId = source.readLong();
+ mMasterKeyId = source.readLong();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- dest.writeLong(mRingMasterKeyId);
+ dest.writeLong(mMasterKeyId);
}
public static Creator<EditKeyResult> CREATOR = new Creator<EditKeyResult>() {
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 70d999242..f79900aab 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);
}
@@ -343,6 +327,18 @@ public abstract class OperationResult implements Parcelable {
MSG_IP_UID_REORDER(LogLevel.DEBUG, R.string.msg_ip_uid_reorder),
MSG_IP_UID_PROCESSING (LogLevel.DEBUG, R.string.msg_ip_uid_processing),
MSG_IP_UID_REVOKED (LogLevel.DEBUG, R.string.msg_ip_uid_revoked),
+ MSG_IP_UAT_CLASSIFYING (LogLevel.DEBUG, R.string.msg_ip_uat_classifying),
+ MSG_IP_UAT_PROCESSING_IMAGE (LogLevel.DEBUG, R.string.msg_ip_uat_processing_image),
+ MSG_IP_UAT_PROCESSING_UNKNOWN (LogLevel.DEBUG, R.string.msg_ip_uat_processing_unknown),
+ MSG_IP_UAT_REVOKED (LogLevel.DEBUG, R.string.msg_ip_uat_revoked),
+ MSG_IP_UAT_CERT_BAD (LogLevel.WARN, R.string.msg_ip_uat_cert_bad),
+ MSG_IP_UAT_CERT_OLD (LogLevel.DEBUG, R.string.msg_ip_uat_cert_old),
+ MSG_IP_UAT_CERT_NONREVOKE (LogLevel.DEBUG, R.string.msg_ip_uat_cert_nonrevoke),
+ MSG_IP_UAT_CERT_NEW (LogLevel.DEBUG, R.string.msg_ip_uat_cert_new),
+ MSG_IP_UAT_CERT_ERROR (LogLevel.WARN, R.string.msg_ip_uat_cert_error),
+ MSG_IP_UAT_CERTS_UNKNOWN (LogLevel.DEBUG, R.plurals.msg_ip_uat_certs_unknown),
+ MSG_IP_UAT_CERT_GOOD_REVOKE (LogLevel.DEBUG, R.string.msg_ip_uat_cert_good_revoke),
+ MSG_IP_UAT_CERT_GOOD (LogLevel.DEBUG, R.string.msg_ip_uat_cert_good),
// import secret
MSG_IS(LogLevel.START, R.string.msg_is),
@@ -359,6 +355,7 @@ public abstract class OperationResult implements Parcelable {
MSG_IS_SUBKEY_STRIPPED (LogLevel.DEBUG, R.string.msg_is_subkey_stripped),
MSG_IS_SUBKEY_DIVERT (LogLevel.DEBUG, R.string.msg_is_subkey_divert),
MSG_IS_SUBKEY_EMPTY (LogLevel.DEBUG, R.string.msg_is_subkey_empty),
+ MSG_IS_SUBKEY_PIN (LogLevel.DEBUG, R.string.msg_is_subkey_pin),
MSG_IS_SUCCESS_IDENTICAL (LogLevel.OK, R.string.msg_is_success_identical),
MSG_IS_SUCCESS (LogLevel.OK, R.string.msg_is_success),
@@ -370,13 +367,16 @@ public abstract class OperationResult implements Parcelable {
MSG_KC_ERROR_MASTER_ALGO (LogLevel.ERROR, R.string.msg_kc_error_master_algo),
MSG_KC_ERROR_DUP_KEY (LogLevel.ERROR, R.string.msg_kc_error_dup_key),
MSG_KC_MASTER (LogLevel.DEBUG, R.string.msg_kc_master),
- MSG_KC_REVOKE_BAD_ERR (LogLevel.WARN, R.string.msg_kc_revoke_bad_err),
- MSG_KC_REVOKE_BAD_LOCAL (LogLevel.WARN, R.string.msg_kc_revoke_bad_local),
- MSG_KC_REVOKE_BAD_TIME (LogLevel.WARN, R.string.msg_kc_revoke_bad_time),
- MSG_KC_REVOKE_BAD_TYPE (LogLevel.WARN, R.string.msg_kc_revoke_bad_type),
- MSG_KC_REVOKE_BAD_TYPE_UID (LogLevel.WARN, R.string.msg_kc_revoke_bad_type_uid),
- MSG_KC_REVOKE_BAD (LogLevel.WARN, R.string.msg_kc_revoke_bad),
+ MSG_KC_MASTER_BAD_TYPE(LogLevel.WARN, R.string.msg_kc_master_bad_type),
+ MSG_KC_MASTER_BAD_LOCAL(LogLevel.WARN, R.string.msg_kc_master_bad_local),
+ MSG_KC_MASTER_BAD_ERR(LogLevel.WARN, R.string.msg_kc_master_bad_err),
+ MSG_KC_MASTER_BAD_TIME(LogLevel.WARN, R.string.msg_kc_master_bad_time),
+ MSG_KC_MASTER_BAD_TYPE_UID(LogLevel.WARN, R.string.msg_kc_master_bad_type_uid),
+ MSG_KC_MASTER_BAD(LogLevel.WARN, R.string.msg_kc_master_bad),
+ MSG_KC_MASTER_LOCAL(LogLevel.WARN, R.string.msg_kc_master_local),
MSG_KC_REVOKE_DUP (LogLevel.DEBUG, R.string.msg_kc_revoke_dup),
+ MSG_KC_NOTATION_DUP (LogLevel.DEBUG, R.string.msg_kc_notation_dup),
+ MSG_KC_NOTATION_EMPTY (LogLevel.DEBUG, R.string.msg_kc_notation_empty),
MSG_KC_SUB (LogLevel.DEBUG, R.string.msg_kc_sub),
MSG_KC_SUB_BAD(LogLevel.WARN, R.string.msg_kc_sub_bad),
MSG_KC_SUB_BAD_ERR(LogLevel.WARN, R.string.msg_kc_sub_bad_err),
@@ -412,6 +412,21 @@ public abstract class OperationResult implements Parcelable {
MSG_KC_UID_REVOKE_OLD (LogLevel.DEBUG, R.string.msg_kc_uid_revoke_old),
MSG_KC_UID_REMOVE (LogLevel.DEBUG, R.string.msg_kc_uid_remove),
MSG_KC_UID_WARN_ENCODING (LogLevel.WARN, R.string.msg_kc_uid_warn_encoding),
+ MSG_KC_UAT_JPEG (LogLevel.DEBUG, R.string.msg_kc_uat_jpeg),
+ MSG_KC_UAT_UNKNOWN (LogLevel.DEBUG, R.string.msg_kc_uat_unknown),
+ MSG_KC_UAT_BAD_ERR (LogLevel.WARN, R.string.msg_kc_uat_bad_err),
+ MSG_KC_UAT_BAD_LOCAL (LogLevel.WARN, R.string.msg_kc_uat_bad_local),
+ MSG_KC_UAT_BAD_TIME (LogLevel.WARN, R.string.msg_kc_uat_bad_time),
+ MSG_KC_UAT_BAD_TYPE (LogLevel.WARN, R.string.msg_kc_uat_bad_type),
+ MSG_KC_UAT_BAD (LogLevel.WARN, R.string.msg_kc_uat_bad),
+ MSG_KC_UAT_CERT_DUP (LogLevel.DEBUG, R.string.msg_kc_uat_cert_dup),
+ MSG_KC_UAT_DUP (LogLevel.DEBUG, R.string.msg_kc_uat_dup),
+ MSG_KC_UAT_FOREIGN (LogLevel.DEBUG, R.string.msg_kc_uat_foreign),
+ MSG_KC_UAT_NO_CERT (LogLevel.DEBUG, R.string.msg_kc_uat_no_cert),
+ MSG_KC_UAT_REVOKE_DUP (LogLevel.DEBUG, R.string.msg_kc_uat_revoke_dup),
+ MSG_KC_UAT_REVOKE_OLD (LogLevel.DEBUG, R.string.msg_kc_uat_revoke_old),
+ MSG_KC_UAT_REMOVE (LogLevel.DEBUG, R.string.msg_kc_uat_remove),
+ MSG_KC_UAT_WARN_ENCODING (LogLevel.WARN, R.string.msg_kc_uat_warn_encoding),
// keyring consolidation
@@ -442,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),
@@ -454,10 +470,13 @@ 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),
MSG_MF_MASTER (LogLevel.DEBUG, R.string.msg_mf_master),
+ MSG_MF_NOTATION_PIN (LogLevel.DEBUG, R.string.msg_mf_notation_pin),
+ MSG_MF_NOTATION_EMPTY (LogLevel.DEBUG, R.string.msg_mf_notation_empty),
MSG_MF_PASSPHRASE (LogLevel.INFO, R.string.msg_mf_passphrase),
MSG_MF_PASSPHRASE_KEY (LogLevel.DEBUG, R.string.msg_mf_passphrase_key),
MSG_MF_PASSPHRASE_EMPTY_RETRY (LogLevel.DEBUG, R.string.msg_mf_passphrase_empty_retry),
@@ -474,6 +493,9 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_UID_PRIMARY (LogLevel.INFO, R.string.msg_mf_uid_primary),
MSG_MF_UID_REVOKE (LogLevel.INFO, R.string.msg_mf_uid_revoke),
MSG_MF_UID_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_mf_uid_error_empty),
+ MSG_MF_UAT_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_mf_uat_error_empty),
+ MSG_MF_UAT_ADD_IMAGE (LogLevel.INFO, R.string.msg_mf_uat_add_image),
+ MSG_MF_UAT_ADD_UNKNOWN (LogLevel.INFO, R.string.msg_mf_uat_add_unknown),
MSG_MF_UNLOCK_ERROR (LogLevel.ERROR, R.string.msg_mf_unlock_error),
MSG_MF_UNLOCK (LogLevel.DEBUG, R.string.msg_mf_unlock),
@@ -491,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),
@@ -502,6 +525,21 @@ public abstract class OperationResult implements Parcelable {
MSG_CON_WARN_DELETE_PUBLIC (LogLevel.WARN, R.string.msg_con_warn_delete_public),
MSG_CON_WARN_DELETE_SECRET (LogLevel.WARN, R.string.msg_con_warn_delete_secret),
+ // edit key (higher level operation than modify)
+ MSG_ED (LogLevel.START, R.string.msg_ed),
+ MSG_ED_CACHING_NEW (LogLevel.DEBUG, R.string.msg_ed_caching_new),
+ MSG_ED_ERROR_NO_PARCEL (LogLevel.ERROR, R.string.msg_ed_error_no_parcel),
+ MSG_ED_ERROR_KEY_NOT_FOUND (LogLevel.ERROR, R.string.msg_ed_error_key_not_found),
+ 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),
@@ -511,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),
@@ -525,6 +565,7 @@ public abstract class OperationResult implements Parcelable {
MSG_DC_ERROR_BAD_PASSPHRASE (LogLevel.ERROR, R.string.msg_dc_error_bad_passphrase),
MSG_DC_ERROR_EXTRACT_KEY (LogLevel.ERROR, R.string.msg_dc_error_extract_key),
MSG_DC_ERROR_INTEGRITY_CHECK (LogLevel.ERROR, R.string.msg_dc_error_integrity_check),
+ MSG_DC_ERROR_INTEGRITY_MISSING (LogLevel.ERROR, R.string.msg_dc_error_integrity_missing),
MSG_DC_ERROR_INVALID_SIGLIST(LogLevel.ERROR, R.string.msg_dc_error_invalid_siglist),
MSG_DC_ERROR_IO (LogLevel.ERROR, R.string.msg_dc_error_io),
MSG_DC_ERROR_NO_DATA (LogLevel.ERROR, R.string.msg_dc_error_no_data),
@@ -555,32 +596,47 @@ public abstract class OperationResult implements Parcelable {
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),
MSG_CRT_CERTIFY_SOME (LogLevel.DEBUG, R.plurals.msg_crt_certify_some),
+ MSG_CRT_ERROR_SELF (LogLevel.ERROR, R.string.msg_crt_error_self),
MSG_CRT_ERROR_MASTER_NOT_FOUND (LogLevel.ERROR, R.string.msg_crt_error_master_not_found),
MSG_CRT_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_crt_error_nothing),
MSG_CRT_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_crt_error_unlock),
@@ -608,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),
@@ -622,6 +679,7 @@ public abstract class OperationResult implements Parcelable {
MSG_EXPORT_ERROR_STORAGE (LogLevel.ERROR, R.string.msg_export_error_storage),
MSG_EXPORT_ERROR_DB (LogLevel.ERROR, R.string.msg_export_error_db),
MSG_EXPORT_ERROR_IO (LogLevel.ERROR, R.string.msg_export_error_io),
+ MSG_EXPORT_ERROR_KEY (LogLevel.ERROR, R.string.msg_export_error_key),
MSG_EXPORT_SUCCESS (LogLevel.OK, R.string.msg_export_success),
MSG_CRT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_crt_upload_success),
@@ -688,7 +746,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) {
@@ -713,7 +771,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;
}
@@ -722,7 +780,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/PgpEditKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java
new file mode 100644
index 000000000..611353ac9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+
+public class PgpEditKeyResult extends OperationResult {
+
+ private transient UncachedKeyRing mRing;
+ public final long mRingMasterKeyId;
+
+ public PgpEditKeyResult(int result, OperationLog log,
+ UncachedKeyRing ring) {
+ super(result, log);
+ mRing = ring;
+ mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none;
+ }
+
+ public UncachedKeyRing getRing() {
+ return mRing;
+ }
+
+ public PgpEditKeyResult(Parcel source) {
+ super(source);
+ mRingMasterKeyId = source.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeLong(mRingMasterKeyId);
+ }
+
+ public static Creator<PgpEditKeyResult> CREATOR = new Creator<PgpEditKeyResult>() {
+ public PgpEditKeyResult createFromParcel(final Parcel source) {
+ return new PgpEditKeyResult(source);
+ }
+
+ public PgpEditKeyResult[] newArray(final int size) {
+ return new PgpEditKeyResult[size];
+ }
+ };
+
+}
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 f9fa41528..40f2f48ad 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -36,19 +36,16 @@ import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
-import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SignatureException;
import java.util.Date;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -83,7 +80,8 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
}
public enum SecretKeyType {
- UNAVAILABLE(0), GNU_DUMMY(1), PASSPHRASE(2), PASSPHRASE_EMPTY(3), DIVERT_TO_CARD(4);
+ UNAVAILABLE(0), GNU_DUMMY(1), PASSPHRASE(2), PASSPHRASE_EMPTY(3), DIVERT_TO_CARD(4), PIN(5),
+ PATTERN(6);
final int mNum;
@@ -101,6 +99,10 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
return PASSPHRASE_EMPTY;
case 4:
return DIVERT_TO_CARD;
+ case 5:
+ return PIN;
+ case 6:
+ return PATTERN;
// if this case happens, it's probably a check from a database value
default:
return UNAVAILABLE;
@@ -135,6 +137,11 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
// It means the passphrase is empty
return SecretKeyType.PASSPHRASE_EMPTY;
} catch (PGPException e) {
+ HashMap<String,String> notation = getRing().getLocalNotationData();
+ if (notation.containsKey("unlock.pin@sufficientlysecure.org")
+ && "1".equals(notation.get("unlock.pin@sufficientlysecure.org"))) {
+ return SecretKeyType.PIN;
+ }
// Otherwise, it's just a regular ol' passphrase
return SecretKeyType.PASSPHRASE;
}
@@ -175,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
@@ -240,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;
@@ -255,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);
}
}
@@ -289,6 +294,12 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
+ if (!isMasterKey()) {
+ throw new AssertionError("tried to certify with non-master key, this is a programming error!");
+ }
+ if (publicKeyRing.getMasterKeyId() == getKeyId()) {
+ throw new AssertionError("key tried to self-certify, this is a programming error!");
+ }
// create a signatureGenerator from the supplied masterKeyId and passphrase
PGPSignatureGenerator signatureGenerator;
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 e20155cc6..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,25 +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.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.PGPSignature;
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.HashSet;
+import java.util.HashMap;
import java.util.Iterator;
public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
@@ -92,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();
@@ -112,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();
@@ -130,4 +124,16 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
});
}
+ public HashMap<String,String> getLocalNotationData() {
+ HashMap<String,String> result = new HashMap<>();
+ Iterator<PGPSignature> it = getRing().getPublicKey().getKeySignatures();
+ while (it.hasNext()) {
+ WrappedSignature sig = new WrappedSignature(it.next());
+ if (sig.isLocal()) {
+ result.putAll(sig.getNotation());
+ }
+ }
+ return result;
+ }
+
}
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 fc5064e79..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
@@ -84,6 +83,10 @@ public class OpenPgpSignatureResultBuilder {
this.mUserIds = userIds;
}
+ public boolean isValidSignature() {
+ return mValidSignature;
+ }
+
public void initValid(CanonicalizedPublicKeyRing signingRing,
CanonicalizedPublicKey signingKey) {
setSignatureAvailable(true);
@@ -101,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 5589a3521..2ba0b6231 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -29,7 +29,6 @@ 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;
@@ -37,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;
@@ -55,6 +54,9 @@ 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;
@@ -85,10 +87,11 @@ public class PgpDecryptVerify extends BaseOperation {
private Set<Long> mAllowedKeyIds;
private boolean mDecryptMetadataOnly;
private byte[] mDecryptedSessionKey;
+ private byte[] mDetachedSignature;
private String mRequiredSignerFingerprint;
private boolean mSignedLiteralData;
- private PgpDecryptVerify(Builder builder) {
+ protected PgpDecryptVerify(Builder builder) {
super(builder.mContext, builder.mProviderHelper, builder.mProgressable);
// private Constructor can only be called from Builder
@@ -100,6 +103,7 @@ 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;
}
@@ -109,15 +113,16 @@ 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;
@@ -182,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);
}
@@ -192,24 +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 (mSignedLiteralData) {
- return verifySignedLiteralData(aIn, 0);
- } else if (aIn.isClearText()) {
- // a cleartext signature, verify it with the other method
- return verifyCleartextSignature(aIn, 0);
+ if (mDetachedSignature != null) {
+ Log.d(Constants.TAG, "Detached signature present, verifying with this signature only");
+
+ 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();
@@ -233,12 +254,12 @@ public class PgpDecryptVerify extends BaseOperation {
// thinking that the proof-fetching operation is going to take most of the time
updateProgress(R.string.progress_reading_data, 75, 100);
- PGPObjectFactory pgpF = new PGPObjectFactory(in, new JcaKeyFingerprintCalculator());
+ JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
Object o = pgpF.nextObject();
if (o instanceof PGPCompressedData) {
log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1);
- pgpF = new PGPObjectFactory(((PGPCompressedData) o).getDataStream(), new JcaKeyFingerprintCalculator());
+ pgpF = new JcaPGPObjectFactory(((PGPCompressedData) o).getDataStream());
o = pgpF.nextObject();
updateProgress(R.string.progress_decompressing_data, 80, 100);
}
@@ -363,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();
@@ -392,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();
@@ -415,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;
}
@@ -441,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;
}
}
@@ -456,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);
@@ -480,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;
}
@@ -496,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);
}
@@ -541,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);
}
@@ -559,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);
@@ -570,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;
@@ -585,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) {
@@ -649,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;
@@ -689,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
@@ -708,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;
}
@@ -730,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) {
@@ -745,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) {
@@ -764,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);
}
@@ -777,8 +820,6 @@ public class PgpDecryptVerify extends BaseOperation {
metadata = null;
}
- OpenPgpSignatureResult signatureResult = signatureResultBuilder.build();
-
if (encryptedData.isIntegrityProtected()) {
updateProgress(R.string.progress_verifying_integrity, 95, 100);
@@ -792,9 +833,9 @@ 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!
- if (signatureResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED
- && signatureResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED) {
- log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent);
+ 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);
}
}
@@ -807,7 +848,8 @@ public class PgpDecryptVerify extends BaseOperation {
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setDecryptMetadata(metadata);
- result.setSignatureResult(signatureResult);
+ result.setSignatureResult(signatureResultBuilder.build());
+ result.setCharset(charset);
return result;
}
@@ -830,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);
@@ -850,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) {
@@ -861,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 {
@@ -947,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
*/
@@ -968,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;
}
}
@@ -985,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);
@@ -997,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 6e45fab99..1a251eb79 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -34,6 +34,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
@@ -49,9 +50,10 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -133,7 +135,7 @@ public class PgpKeyOperation {
public PgpKeyOperation(Progressable progress) {
super();
if (progress != null) {
- mProgress = new Stack<Progressable>();
+ mProgress = new Stack<>();
mProgress.push(progress);
}
}
@@ -286,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);
@@ -300,7 +300,7 @@ public class PgpKeyOperation {
}
}
- public EditKeyResult createSecretKeyRing(SaveKeyringParcel saveParcel) {
+ public PgpEditKeyResult createSecretKeyRing(SaveKeyringParcel saveParcel) {
OperationLog log = new OperationLog();
int indent = 0;
@@ -313,23 +313,23 @@ public class PgpKeyOperation {
if (saveParcel.mAddSubKeys.isEmpty()) {
log.add(LogType.MSG_CR_ERROR_NO_MASTER, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (saveParcel.mAddUserIds.isEmpty()) {
log.add(LogType.MSG_CR_ERROR_NO_USER_ID, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
SubkeyAdd add = saveParcel.mAddSubKeys.remove(0);
if ((add.mFlags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
log.add(LogType.MSG_CR_ERROR_NO_CERTIFY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (add.mExpiry == null) {
log.add(LogType.MSG_CR_ERROR_NULL_EXPIRY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
subProgressPush(10, 30);
@@ -338,7 +338,7 @@ public class PgpKeyOperation {
// return null if this failed (an error will already have been logged by createKey)
if (keyPair == null) {
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
progress(R.string.progress_building_master_key, 40);
@@ -365,10 +365,10 @@ public class PgpKeyOperation {
} catch (PGPException e) {
log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
Log.e(Constants.TAG, "pgp error encoding key", e);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} catch (IOException e) {
Log.e(Constants.TAG, "io error encoding key", e);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -387,8 +387,11 @@ 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 EditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
+ public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
String passphrase) {
OperationLog log = new OperationLog();
@@ -413,7 +416,7 @@ public class PgpKeyOperation {
// Make sure this is called with a proper SaveKeyringParcel
if (saveParcel.mMasterKeyId == null || saveParcel.mMasterKeyId != wsKR.getMasterKeyId()) {
log.add(LogType.MSG_MF_ERROR_KEYID, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// We work on bouncycastle object level here
@@ -424,21 +427,26 @@ public class PgpKeyOperation {
if (saveParcel.mFingerprint == null || !Arrays.equals(saveParcel.mFingerprint,
masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogType.MSG_MF_ERROR_FINGERPRINT, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ 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);
}
- private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
+ private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
int masterKeyFlags, long masterKeyExpiry,
SaveKeyringParcel saveParcel, String passphrase,
OperationLog log) {
@@ -460,7 +468,7 @@ public class PgpKeyOperation {
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -469,7 +477,7 @@ public class PgpKeyOperation {
// Check if we were cancelled
if (checkCancelled()) {
log.add(LogType.MSG_OPERATION_CANCELLED, indent);
- return new EditKeyResult(EditKeyResult.RESULT_CANCELLED, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
}
{ // work on master secret key
@@ -477,7 +485,7 @@ public class PgpKeyOperation {
PGPPublicKey modifiedPublicKey = masterPublicKey;
// 2a. Add certificates for new user ids
- subProgressPush(15, 25);
+ subProgressPush(15, 23);
for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size()));
@@ -486,7 +494,7 @@ public class PgpKeyOperation {
if (userId.equals("")) {
log.add(LogType.MSG_MF_UID_ERROR_EMPTY, indent + 1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// this operation supersedes all previous binding and revocation certificates,
@@ -494,11 +502,11 @@ 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);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
|| cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
@@ -521,8 +529,37 @@ public class PgpKeyOperation {
}
subProgressPop();
- // 2b. Add revocations for revoked user ids
- subProgressPush(25, 40);
+ // 2b. Add certificates for new user ids
+ subProgressPush(23, 32);
+ for (int i = 0; i < saveParcel.mAddUserAttribute.size(); i++) {
+
+ progress(R.string.progress_modify_adduat, (i - 1) * (100 / saveParcel.mAddUserAttribute.size()));
+ WrappedUserAttribute attribute = saveParcel.mAddUserAttribute.get(i);
+
+ switch (attribute.getType()) {
+ // the 'none' type must not succeed
+ case WrappedUserAttribute.UAT_NONE:
+ log.add(LogType.MSG_MF_UAT_ERROR_EMPTY, indent);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ case WrappedUserAttribute.UAT_IMAGE:
+ log.add(LogType.MSG_MF_UAT_ADD_IMAGE, indent);
+ break;
+ default:
+ log.add(LogType.MSG_MF_UAT_ADD_UNKNOWN, indent);
+ break;
+ }
+
+ PGPUserAttributeSubpacketVector vector = attribute.getVector();
+
+ // generate and add new certificate
+ PGPSignature cert = generateUserAttributeSignature(masterPrivateKey,
+ masterPublicKey, vector);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert);
+ }
+ subProgressPop();
+
+ // 2c. Add revocations for revoked user ids
+ subProgressPush(32, 40);
for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / saveParcel.mRevokeUserIds.size()));
@@ -540,7 +577,7 @@ public class PgpKeyOperation {
}
if (!exists) {
log.add(LogType.MSG_MF_ERROR_NOEXIST_REVOKE, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// a duplicate revocation will be removed during canonicalization, so no need to
@@ -571,7 +608,7 @@ public class PgpKeyOperation {
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
// foreign certificate?! error error error
log.add(LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// we know from canonicalization that if there is any revocation here, it
// is valid and not superseded by a newer certification.
@@ -592,7 +629,7 @@ public class PgpKeyOperation {
if (currentCert == null) {
// no certificate found?! error error error
log.add(LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// we definitely should not update certifications of revoked keys, so just leave it.
@@ -600,7 +637,7 @@ public class PgpKeyOperation {
// revoked user ids cannot be primary!
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
log.add(LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
continue;
}
@@ -649,7 +686,7 @@ public class PgpKeyOperation {
if (!ok) {
log.add(LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -665,7 +702,7 @@ public class PgpKeyOperation {
// Check if we were cancelled - again
if (checkCancelled()) {
log.add(LogType.MSG_OPERATION_CANCELLED, indent);
- return new EditKeyResult(EditKeyResult.RESULT_CANCELLED, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
}
// 4a. For each subkey change, generate new subkey binding certificate
@@ -681,7 +718,28 @@ public class PgpKeyOperation {
if (sKey == null) {
log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ 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
@@ -689,7 +747,7 @@ public class PgpKeyOperation {
new Date(change.mExpiry*1000).before(new Date())) {
log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY,
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// if this is the master key, update uid certificates instead
@@ -699,7 +757,7 @@ public class PgpKeyOperation {
if ((flags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
log.add(LogType.MSG_MF_ERROR_NO_CERTIFY, indent + 1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
PGPPublicKey pKey =
@@ -707,7 +765,7 @@ public class PgpKeyOperation {
flags, expiry, indent, log);
if (pKey == null) {
// error log entry has already been added by updateMasterCertificates itself
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
masterSecretKey = PGPSecretKey.replacePublicKey(sKey, pKey);
masterPublicKey = pKey;
@@ -762,7 +820,7 @@ public class PgpKeyOperation {
if (sKey == null) {
log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent+1, KeyFormattingUtils.convertKeyIdToHex(revocation));
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
PGPPublicKey pKey = sKey.getPublicKey();
@@ -774,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 EditKeyResult(EditKeyResult.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++) {
@@ -805,7 +839,7 @@ public class PgpKeyOperation {
// Check if we were cancelled - again. This operation is expensive so we do it each loop.
if (checkCancelled()) {
log.add(LogType.MSG_OPERATION_CANCELLED, indent);
- return new EditKeyResult(EditKeyResult.RESULT_CANCELLED, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
}
progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
@@ -815,12 +849,12 @@ public class PgpKeyOperation {
if (add.mExpiry == null) {
log.add(LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (add.mExpiry > 0L && new Date(add.mExpiry*1000).before(new Date())) {
log.add(LogType.MSG_MF_ERROR_PAST_EXPIRY, indent +1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// generate a new secret key (privkey only for now)
@@ -832,7 +866,7 @@ public class PgpKeyOperation {
subProgressPop();
if (keyPair == null) {
log.add(LogType.MSG_MF_ERROR_PGP, indent +1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// add subkey binding signature (making this a sub rather than master key)
@@ -867,67 +901,20 @@ public class PgpKeyOperation {
// Check if we were cancelled - again. This operation is expensive so we do it each loop.
if (checkCancelled()) {
log.add(LogType.MSG_OPERATION_CANCELLED, indent);
- return new EditKeyResult(EditKeyResult.RESULT_CANCELLED, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
}
// 6. If requested, change passphrase
- if (saveParcel.mNewPassphrase != null) {
+ if (saveParcel.mNewUnlock != null) {
progress(R.string.progress_modify_passphrase, 90);
log.add(LogType.MSG_MF_PASSPHRASE, indent);
indent += 1;
- PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
- .get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- // Build key encryptor based on new passphrase
- PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
- SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- saveParcel.mNewPassphrase.toCharArray());
-
- // noinspection unchecked
- for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
- log.add(LogType.MSG_MF_PASSPHRASE_KEY, indent,
- KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID()));
-
- boolean ok = false;
-
- try {
- // try to set new passphrase
- sKey = PGPSecretKey.copyWithNewPassword(sKey, keyDecryptor, keyEncryptorNew);
- ok = true;
- } catch (PGPException e) {
-
- // if this is the master key, error!
- if (sKey.getKeyID() == masterPublicKey.getKeyID()) {
- log.add(LogType.MSG_MF_ERROR_PASSPHRASE_MASTER, indent+1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
- }
-
- // being in here means decrypt failed, likely due to a bad passphrase try
- // again with an empty passphrase, maybe we can salvage this
- try {
- log.add(LogType.MSG_MF_PASSPHRASE_EMPTY_RETRY, indent+1);
- PBESecretKeyDecryptor emptyDecryptor =
- new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
- sKey = PGPSecretKey.copyWithNewPassword(sKey, emptyDecryptor, keyEncryptorNew);
- ok = true;
- } catch (PGPException e2) {
- // non-fatal but not ok, handled below
- }
- }
-
- if (!ok) {
- // for a subkey, it's merely a warning
- log.add(LogType.MSG_MF_PASSPHRASE_FAIL, indent+1,
- KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID()));
- continue;
- }
-
- sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
-
+ sKR = applyNewUnlock(sKR, masterPublicKey, masterPrivateKey,
+ passphrase, saveParcel.mNewUnlock, log, indent);
+ if (sKR == null) {
+ // The error has been logged above, just return a bad state
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
indent -= 1;
@@ -936,20 +923,235 @@ public class PgpKeyOperation {
} catch (IOException e) {
Log.e(Constants.TAG, "encountered IOException while modifying key", e);
log.add(LogType.MSG_MF_ERROR_ENCODE, indent+1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} catch (PGPException e) {
Log.e(Constants.TAG, "encountered pgp error while modifying key", e);
log.add(LogType.MSG_MF_ERROR_PGP, indent+1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} catch (SignatureException e) {
Log.e(Constants.TAG, "encountered SignatureException while modifying key", e);
log.add(LogType.MSG_MF_ERROR_SIG, indent+1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
progress(R.string.progress_done, 100);
log.add(LogType.MSG_MF_SUCCESS, indent);
- return new EditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR));
+ return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR));
+
+ }
+
+ /** 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,
+ PGPPrivateKey masterPrivateKey,
+ String passphrase,
+ ChangeUnlockParcel newUnlock,
+ OperationLog log, int indent) throws PGPException {
+
+ if (newUnlock.mNewPassphrase != null) {
+ sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPassphrase, log, indent);
+
+ // if there is any old packet with notation data
+ if (hasNotationData(sKR)) {
+
+ log.add(LogType.MSG_MF_NOTATION_EMPTY, indent);
+
+ // add packet with EMPTY notation data (updates old one, but will be stripped later)
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ { // set subpackets
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen.setExportable(false, false);
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ }
+ sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
+ PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
+
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR,
+ PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey));
+ }
+
+ return sKR;
+ }
+
+ if (newUnlock.mNewPin != null) {
+ sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPin, log, indent);
+
+ log.add(LogType.MSG_MF_NOTATION_PIN, indent);
+
+ // add packet with "pin" notation data
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ { // set subpackets
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen.setExportable(false, false);
+ hashedPacketsGen.setNotationData(false, true, "unlock.pin@sufficientlysecure.org", "1");
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ }
+ sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
+ PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
+
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR,
+ PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey));
+
+ return sKR;
+ }
+
+ throw new UnsupportedOperationException("PIN passphrases not yet implemented!");
+
+ }
+
+ /** This method returns true iff the provided keyring has a local direct key signature
+ * with notation data.
+ */
+ private static boolean hasNotationData(PGPSecretKeyRing sKR) {
+ // noinspection unchecked
+ Iterator<PGPSignature> sigs = sKR.getPublicKey().getKeySignatures();
+ while (sigs.hasNext()) {
+ WrappedSignature sig = new WrappedSignature(sigs.next());
+ if (sig.getSignatureType() == PGPSignature.DIRECT_KEY
+ && sig.isLocal() && !sig.getNotation().isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static PGPSecretKeyRing applyNewPassphrase(
+ PGPSecretKeyRing sKR,
+ PGPPublicKey masterPublicKey,
+ String passphrase,
+ String newPassphrase,
+ OperationLog log, int indent) throws PGPException {
+
+ PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
+ .get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ // Build key encryptor based on new passphrase
+ PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
+ SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ newPassphrase.toCharArray());
+
+ // noinspection unchecked
+ for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
+ log.add(LogType.MSG_MF_PASSPHRASE_KEY, indent,
+ KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID()));
+
+ boolean ok = false;
+
+ try {
+ // try to set new passphrase
+ sKey = PGPSecretKey.copyWithNewPassword(sKey, keyDecryptor, keyEncryptorNew);
+ ok = true;
+ } catch (PGPException e) {
+
+ // if this is the master key, error!
+ if (sKey.getKeyID() == masterPublicKey.getKeyID()) {
+ log.add(LogType.MSG_MF_ERROR_PASSPHRASE_MASTER, indent+1);
+ return null;
+ }
+
+ // being in here means decrypt failed, likely due to a bad passphrase try
+ // again with an empty passphrase, maybe we can salvage this
+ try {
+ log.add(LogType.MSG_MF_PASSPHRASE_EMPTY_RETRY, indent+1);
+ PBESecretKeyDecryptor emptyDecryptor =
+ new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ sKey = PGPSecretKey.copyWithNewPassword(sKey, emptyDecryptor, keyEncryptorNew);
+ ok = true;
+ } catch (PGPException e2) {
+ // non-fatal but not ok, handled below
+ }
+ }
+
+ if (!ok) {
+ // for a subkey, it's merely a warning
+ log.add(LogType.MSG_MF_PASSPHRASE_FAIL, indent+1,
+ KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID()));
+ continue;
+ }
+
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+
+ }
+
+ return sKR;
}
@@ -1072,6 +1274,26 @@ public class PgpKeyOperation {
return sGen.generateCertification(userId, pKey);
}
+ private static PGPSignature generateUserAttributeSignature(
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey,
+ PGPUserAttributeSubpacketVector vector)
+ throws IOException, PGPException, SignatureException {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ {
+ /* critical subpackets: we consider those important for a modern pgp implementation */
+ hashedPacketsGen.setSignatureCreationTime(true, new Date());
+ }
+
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ return sGen.generateCertification(vector, pKey);
+ }
+
private static PGPSignature generateRevocationSignature(
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
throws IOException, PGPException, SignatureException {
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 f89027a19..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 {
}
}
- private 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 d05ce3d5c..681aff56d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.SignatureSubpacketTags;
+import org.spongycastle.bcpg.UserAttributeSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
@@ -30,18 +31,18 @@ import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
+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;
import org.sufficientlysecure.keychain.util.Utf8Util;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -49,11 +50,13 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.TimeZone;
import java.util.TreeSet;
/** Wrapper around PGPKeyRing class, to be constructed from bytes.
@@ -136,7 +139,7 @@ public class UncachedKeyRing {
public static UncachedKeyRing decodeFromData(byte[] data)
throws PgpGeneralException, IOException {
- Iterator<UncachedKeyRing> parsed = fromStream(new ByteArrayInputStream(data));
+ IteratorWithIOThrow<UncachedKeyRing> parsed = fromStream(new ByteArrayInputStream(data));
if ( ! parsed.hasNext()) {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
@@ -152,14 +155,14 @@ public class UncachedKeyRing {
}
- public static Iterator<UncachedKeyRing> fromStream(final InputStream stream) throws IOException {
+ public static IteratorWithIOThrow<UncachedKeyRing> fromStream(final InputStream stream) {
- return new Iterator<UncachedKeyRing>() {
+ return new IteratorWithIOThrow<UncachedKeyRing>() {
UncachedKeyRing mNext = null;
PGPObjectFactory mObjectFactory = null;
- private void cacheNext() {
+ private void cacheNext() throws IOException {
if (mNext != null) {
return;
}
@@ -188,21 +191,19 @@ public class UncachedKeyRing {
// if we are past the while loop, that means the objectFactory had no next
mObjectFactory = null;
}
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException while processing stream. ArmoredInputStream CRC check failed?", e);
} catch (ArrayIndexOutOfBoundsException e) {
- Log.e(Constants.TAG, "ArmoredInputStream decode failed, symbol is not in decodingTable!", e);
+ throw new IOException(e);
}
}
@Override
- public boolean hasNext() {
+ public boolean hasNext() throws IOException {
cacheNext();
return mNext != null;
}
@Override
- public UncachedKeyRing next() {
+ public UncachedKeyRing next() throws IOException {
try {
cacheNext();
return mNext;
@@ -210,15 +211,15 @@ public class UncachedKeyRing {
mNext = null;
}
}
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
};
}
+ public interface IteratorWithIOThrow<E> {
+ public boolean hasNext() throws IOException;
+ public E next() throws IOException;
+ }
+
public void encodeArmored(OutputStream out, String version) throws IOException {
ArmoredOutputStream aos = new ArmoredOutputStream(out);
if (version != null) {
@@ -265,6 +266,35 @@ public class UncachedKeyRing {
*/
@SuppressWarnings("ConstantConditions")
public CanonicalizedKeyRing canonicalize(OperationLog log, int indent) {
+ return canonicalize(log, indent, false);
+ }
+
+
+ /** "Canonicalizes" a public key, removing inconsistencies in the process.
+ *
+ * More specifically:
+ * - Remove all non-verifying self-certificates
+ * - Remove all "future" self-certificates
+ * - Remove all certificates flagged as "local"
+ * - Remove all certificates which are superseded by a newer one on the same target,
+ * including revocations with later re-certifications.
+ * - Remove all certificates in other positions if not of known type:
+ * - key revocation signatures on the master key
+ * - subkey binding signatures for subkeys
+ * - certifications and certification revocations for user ids
+ * - If a subkey retains no valid subkey binding certificate, remove it
+ * - If a user id retains no valid self certificate, remove it
+ * - If the key is a secret key, remove all certificates by foreign keys
+ * - If no valid user id remains, log an error and return null
+ *
+ * This operation writes an OperationLog which can be used as part of an OperationResultParcel.
+ *
+ * @param forExport if this is true, non-exportable signatures will be removed
+ * @return A canonicalized key, or null on fatal error (log will include a message in this case)
+ *
+ */
+ @SuppressWarnings("ConstantConditions")
+ public CanonicalizedKeyRing canonicalize(OperationLog log, int indent, boolean forExport) {
log.add(isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC,
indent, KeyFormattingUtils.convertKeyIdToHex(getMasterKeyId()));
@@ -276,7 +306,10 @@ public class UncachedKeyRing {
return null;
}
- final Date now = new Date();
+ Calendar nowCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ // allow for diverging clocks up to one day when checking creation time
+ nowCal.add(Calendar.DAY_OF_YEAR, 1);
+ final Date nowPlusOneDay = nowCal.getTime();
int redundantCerts = 0, badCerts = 0;
@@ -297,6 +330,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = masterKey;
PGPSignature revocation = null;
+ PGPSignature notation = null;
for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {
int type = zert.getSignatureType();
@@ -306,47 +340,80 @@ public class UncachedKeyRing {
|| type == PGPSignature.CASUAL_CERTIFICATION
|| type == PGPSignature.POSITIVE_CERTIFICATION
|| type == PGPSignature.CERTIFICATION_REVOCATION) {
- log.add(LogType.MSG_KC_REVOKE_BAD_TYPE_UID, indent);
+ log.add(LogType.MSG_KC_MASTER_BAD_TYPE_UID, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
}
WrappedSignature cert = new WrappedSignature(zert);
- if (type != PGPSignature.KEY_REVOCATION) {
+ if (type != PGPSignature.KEY_REVOCATION && type != PGPSignature.DIRECT_KEY) {
// Unknown type, just remove
- log.add(LogType.MSG_KC_REVOKE_BAD_TYPE, indent, "0x" + Integer.toString(type, 16));
+ log.add(LogType.MSG_KC_MASTER_BAD_TYPE, indent, "0x" + Integer.toString(type, 16));
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
}
- if (cert.getCreationTime().after(now)) {
+ if (cert.getCreationTime().after(nowPlusOneDay)) {
// Creation date in the future? No way!
- log.add(LogType.MSG_KC_REVOKE_BAD_TIME, indent);
+ log.add(LogType.MSG_KC_MASTER_BAD_TIME, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
}
- if (cert.isLocal()) {
- // Remove revocation certs with "local" flag
- log.add(LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
+ try {
+ cert.init(masterKey);
+ if (!cert.verifySignature(masterKey)) {
+ log.add(LogType.MSG_KC_MASTER_BAD, indent);
+ modified = PGPPublicKey.removeCertification(modified, zert);
+ badCerts += 1;
+ continue;
+ }
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_KC_MASTER_BAD_ERR, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
}
- try {
- cert.init(masterKey);
- if (!cert.verifySignature(masterKey)) {
- log.add(LogType.MSG_KC_REVOKE_BAD, indent);
+ // if this is for export, we always remove any non-exportable certs
+ if (forExport && cert.isLocal()) {
+ // Remove revocation certs with "local" flag
+ log.add(LogType.MSG_KC_MASTER_LOCAL, indent);
+ modified = PGPPublicKey.removeCertification(modified, zert);
+ continue;
+ }
+
+ // special case: non-exportable, direct key signatures for notations!
+ if (cert.getSignatureType() == PGPSignature.DIRECT_KEY) {
+ // must be local, otherwise strip!
+ if (!cert.isLocal()) {
+ log.add(LogType.MSG_KC_MASTER_BAD_TYPE, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
}
- } catch (PgpGeneralException e) {
- log.add(LogType.MSG_KC_REVOKE_BAD_ERR, indent);
+
+ // first notation? fine then.
+ if (notation == null) {
+ notation = zert;
+ // more notations? at least one is superfluous, then.
+ } else if (notation.getCreationTime().before(zert.getCreationTime())) {
+ log.add(LogType.MSG_KC_NOTATION_DUP, indent);
+ modified = PGPPublicKey.removeCertification(modified, notation);
+ redundantCerts += 1;
+ notation = zert;
+ } else {
+ log.add(LogType.MSG_KC_NOTATION_DUP, indent);
+ modified = PGPPublicKey.removeCertification(modified, zert);
+ redundantCerts += 1;
+ }
+ continue;
+ } else if (cert.isLocal()) {
+ // Remove revocation certs with "local" flag
+ log.add(LogType.MSG_KC_MASTER_BAD_LOCAL, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
@@ -368,7 +435,17 @@ public class UncachedKeyRing {
}
}
- ArrayList<String> processedUserIds = new ArrayList<String>();
+ // If we have a notation packet, check if there is even any data in it?
+ if (notation != null) {
+ // If there isn't, might as well strip it
+ if (new WrappedSignature(notation).getNotation().isEmpty()) {
+ log.add(LogType.MSG_KC_NOTATION_EMPTY, indent);
+ modified = PGPPublicKey.removeCertification(modified, notation);
+ redundantCerts += 1;
+ }
+ }
+
+ ArrayList<String> processedUserIds = new ArrayList<>();
for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) {
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
@@ -393,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();
@@ -410,7 +487,7 @@ public class UncachedKeyRing {
continue;
}
- if (cert.getCreationTime().after(now)) {
+ if (cert.getCreationTime().after(nowPlusOneDay)) {
// Creation date in the future? No way!
log.add(LogType.MSG_KC_UID_BAD_TIME, indent);
modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
@@ -530,6 +607,170 @@ public class UncachedKeyRing {
return null;
}
+ ArrayList<PGPUserAttributeSubpacketVector> processedUserAttributes = new ArrayList<>();
+ for (PGPUserAttributeSubpacketVector userAttribute :
+ new IterableIterator<PGPUserAttributeSubpacketVector>(masterKey.getUserAttributes())) {
+
+ if (userAttribute.getSubpacket(UserAttributeSubpacketTags.IMAGE_ATTRIBUTE) != null) {
+ log.add(LogType.MSG_KC_UAT_JPEG, indent);
+ } else {
+ log.add(LogType.MSG_KC_UAT_UNKNOWN, indent);
+ }
+
+ try {
+ indent += 1;
+
+ // check for duplicate user attributes
+ if (processedUserAttributes.contains(userAttribute)) {
+ log.add(LogType.MSG_KC_UAT_DUP, indent);
+ // strip out the first found user id with this name
+ modified = PGPPublicKey.removeCertification(modified, userAttribute);
+ }
+ processedUserAttributes.add(userAttribute);
+
+ PGPSignature selfCert = null;
+ revocation = null;
+
+ // look through signatures for this specific user id
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForUserAttribute(userAttribute);
+ if (signaturesIt != null) {
+ for (PGPSignature zert : new IterableIterator<>(signaturesIt)) {
+ WrappedSignature cert = new WrappedSignature(zert);
+ long certId = cert.getKeyId();
+
+ int type = zert.getSignatureType();
+ if (type != PGPSignature.DEFAULT_CERTIFICATION
+ && type != PGPSignature.NO_CERTIFICATION
+ && type != PGPSignature.CASUAL_CERTIFICATION
+ && type != PGPSignature.POSITIVE_CERTIFICATION
+ && type != PGPSignature.CERTIFICATION_REVOCATION) {
+ log.add(LogType.MSG_KC_UAT_BAD_TYPE,
+ indent, "0x" + Integer.toString(zert.getSignatureType(), 16));
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+
+ if (cert.getCreationTime().after(nowPlusOneDay)) {
+ // Creation date in the future? No way!
+ log.add(LogType.MSG_KC_UAT_BAD_TIME, indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+
+ if (cert.isLocal()) {
+ // Creation date in the future? No way!
+ log.add(LogType.MSG_KC_UAT_BAD_LOCAL, indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+
+ // If this is a foreign signature, ...
+ if (certId != masterKeyId) {
+ // never mind any further for public keys, but remove them from secret ones
+ if (isSecret()) {
+ log.add(LogType.MSG_KC_UAT_FOREIGN,
+ indent, KeyFormattingUtils.convertKeyIdToHex(certId));
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ }
+ continue;
+ }
+
+ // Otherwise, first make sure it checks out
+ try {
+ cert.init(masterKey);
+ if (!cert.verifySignature(masterKey, userAttribute)) {
+ log.add(LogType.MSG_KC_UAT_BAD,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_KC_UAT_BAD_ERR,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+
+ switch (type) {
+ case PGPSignature.DEFAULT_CERTIFICATION:
+ case PGPSignature.NO_CERTIFICATION:
+ case PGPSignature.CASUAL_CERTIFICATION:
+ case PGPSignature.POSITIVE_CERTIFICATION:
+ if (selfCert == null) {
+ selfCert = zert;
+ } else if (selfCert.getCreationTime().before(cert.getCreationTime())) {
+ log.add(LogType.MSG_KC_UAT_CERT_DUP,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, selfCert);
+ redundantCerts += 1;
+ selfCert = zert;
+ } else {
+ log.add(LogType.MSG_KC_UAT_CERT_DUP,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ redundantCerts += 1;
+ }
+ // If there is a revocation certificate, and it's older than this, drop it
+ if (revocation != null
+ && revocation.getCreationTime().before(selfCert.getCreationTime())) {
+ log.add(LogType.MSG_KC_UAT_REVOKE_OLD,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation);
+ revocation = null;
+ redundantCerts += 1;
+ }
+ break;
+
+ case PGPSignature.CERTIFICATION_REVOCATION:
+ // If this is older than the (latest) self cert, drop it
+ if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) {
+ log.add(LogType.MSG_KC_UAT_REVOKE_OLD,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ redundantCerts += 1;
+ continue;
+ }
+ // first revocation? remember it.
+ if (revocation == null) {
+ revocation = zert;
+ // more revocations? at least one is superfluous, then.
+ } else if (revocation.getCreationTime().before(cert.getCreationTime())) {
+ log.add(LogType.MSG_KC_UAT_REVOKE_DUP,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation);
+ redundantCerts += 1;
+ revocation = zert;
+ } else {
+ log.add(LogType.MSG_KC_UAT_REVOKE_DUP,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ redundantCerts += 1;
+ }
+ break;
+ }
+ }
+ }
+
+ // If no valid certificate (if only a revocation) remains, drop it
+ if (selfCert == null && revocation == null) {
+ log.add(LogType.MSG_KC_UAT_REMOVE,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute);
+ }
+
+ } finally {
+ indent -= 1;
+ }
+ }
+
+
// Replace modified key in the keyring
ring = replacePublicKey(ring, modified);
indent -= 1;
@@ -537,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())) {
@@ -592,7 +833,7 @@ public class UncachedKeyRing {
continue;
}
- if (cert.getCreationTime().after(now)) {
+ if (cert.getCreationTime().after(nowPlusOneDay)) {
// Creation date in the future? No way!
log.add(LogType.MSG_KC_SUB_BAD_TIME, indent);
badCerts += 1;
@@ -777,8 +1018,8 @@ public class UncachedKeyRing {
/** This operation merges information from a different keyring, returning a combined
* UncachedKeyRing.
*
- * The combined keyring contains the subkeys and user ids of both input keyrings, but it does
- * not necessarily have the canonicalized property.
+ * The combined keyring contains the subkeys, user ids and user attributes of both input
+ * keyrings, but it does not necessarily have the canonicalized property.
*
* @param other The UncachedKeyRing to merge. Must not be empty, and of the same masterKeyId
* @return A consolidated UncachedKeyRing with the data of both input keyrings. Same type as
@@ -801,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) {
@@ -883,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;
@@ -898,6 +1139,32 @@ public class UncachedKeyRing {
modified = PGPPublicKey.addCertification(modified, rawUserId, cert);
}
}
+
+ // Copy over all user attribute certificates
+ for (PGPUserAttributeSubpacketVector vector :
+ new IterableIterator<PGPUserAttributeSubpacketVector>(key.getUserAttributes())) {
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSignature> signaturesIt = key.getSignaturesForUserAttribute(vector);
+ // no signatures for this user attribute attribute, skip it
+ if (signaturesIt == null) {
+ continue;
+ }
+ for (PGPSignature cert : new IterableIterator<>(signaturesIt)) {
+ // Don't merge foreign stuff into secret keys
+ if (cert.getKeyID() != masterKeyId && isSecret()) {
+ continue;
+ }
+ byte[] encoded = cert.getEncoded();
+ // Known cert, skip it
+ if (certs.contains(encoded)) {
+ continue;
+ }
+ newCerts += 1;
+ certs.add(encoded);
+ modified = PGPPublicKey.addCertification(modified, vector, cert);
+ }
+ }
+
// If anything changed, save the updated (sub)key
if (modified != resultKey) {
result = replacePublicKey(result, modified);
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 c4cacaca7..9276cba10 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -20,10 +20,10 @@ 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;
+import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.IterableIterator;
@@ -50,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();
@@ -60,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;
@@ -135,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
@@ -186,18 +169,20 @@ public class UncachedPublicKey {
}
/**
- * Returns primary user id if existing. If not, return first encountered user id.
+ * Returns primary user id if existing. If not, return first encountered user id. If there
+ * is no user id, return null (this can only happen for not yet canonicalized keys during import)
*/
public String getPrimaryUserIdWithFallback() {
String userId = getPrimaryUserId();
if (userId == null) {
- userId = (String) mPublicKey.getUserIDs().next();
+ Iterator<String> it = mPublicKey.getUserIDs();
+ userId = it.hasNext() ? it.next() : null;
}
return userId;
}
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));
@@ -206,13 +191,22 @@ 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);
}
return userIds;
}
+ public ArrayList<WrappedUserAttribute> getUnorderedUserAttributes() {
+ ArrayList<WrappedUserAttribute> userAttributes = new ArrayList<>();
+ for (PGPUserAttributeSubpacketVector userAttribute :
+ new IterableIterator<PGPUserAttributeSubpacketVector>(mPublicKey.getUserAttributes())) {
+ userAttributes.add(new WrappedUserAttribute(userAttribute));
+ }
+ return userAttributes;
+ }
+
public boolean isElGamalEncrypt() {
return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
}
@@ -268,33 +262,103 @@ public class UncachedPublicKey {
}
}
+ public Iterator<WrappedSignature> getSignaturesForUserAttribute(WrappedUserAttribute attribute) {
+ final Iterator<PGPSignature> it = mPublicKey.getSignaturesForUserAttribute(attribute.getVector());
+ if (it != null) {
+ return new Iterator<WrappedSignature>() {
+ public void remove() {
+ it.remove();
+ }
+ public WrappedSignature next() {
+ return new WrappedSignature(it.next());
+ }
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+
/** Get all key usage flags.
* If at least one key flag subpacket is present return these. If no
* subpacket is present it returns null.
*
* 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 93afb987a..c6fad1a73 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.SignatureSubpacket;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.Exportable;
+import org.spongycastle.bcpg.sig.NotationData;
import org.spongycastle.bcpg.sig.Revocable;
import org.spongycastle.bcpg.sig.RevocationReason;
import org.spongycastle.openpgp.PGPException;
@@ -28,15 +29,16 @@ import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
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;
/** OpenKeychain wrapper around PGPSignature objects.
*
@@ -76,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;
}
@@ -197,12 +203,23 @@ public class WrappedSignature {
}
}
+ boolean verifySignature(PGPPublicKey key, PGPUserAttributeSubpacketVector attribute) throws PgpGeneralException {
+ try {
+ return mSig.verifyCertification(attribute, key);
+ } catch (PGPException e) {
+ throw new PgpGeneralException("Error!", e);
+ }
+ }
+
public boolean verifySignature(UncachedPublicKey key, byte[] rawUserId) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), rawUserId);
}
public boolean verifySignature(CanonicalizedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
+ public boolean verifySignature(UncachedPublicKey key, WrappedUserAttribute attribute) throws PgpGeneralException {
+ return verifySignature(key.getPublicKey(), attribute.getVector());
+ }
public static WrappedSignature fromBytes(byte[] data) {
PGPObjectFactory factory = new PGPObjectFactory(data);
@@ -239,4 +256,20 @@ public class WrappedSignature {
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE);
return ! ((Exportable) p).isExportable();
}
+
+ public HashMap<String,String> getNotation() {
+ HashMap<String,String> result = new HashMap<>();
+
+ // If there is any notation data
+ if (mSig.getHashedSubPackets() != null
+ && mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.NOTATION_DATA)) {
+ // Iterate over notation data
+ for (NotationData data : mSig.getHashedSubPackets().getNotationDataOccurrences()) {
+ result.put(data.getNotationName(), data.getNotationValue());
+ }
+ }
+
+ return result;
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java
new file mode 100644
index 000000000..8e23d36d9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java
@@ -0,0 +1,123 @@
+/*
+ * 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.pgp;
+
+import org.spongycastle.bcpg.BCPGInputStream;
+import org.spongycastle.bcpg.BCPGOutputStream;
+import org.spongycastle.bcpg.Packet;
+import org.spongycastle.bcpg.UserAttributePacket;
+import org.spongycastle.bcpg.UserAttributeSubpacket;
+import org.spongycastle.bcpg.UserAttributeSubpacketInputStream;
+import org.spongycastle.bcpg.UserAttributeSubpacketTags;
+import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+public class WrappedUserAttribute implements Serializable {
+
+ public static final int UAT_NONE = 0;
+ public static final int UAT_IMAGE = UserAttributeSubpacketTags.IMAGE_ATTRIBUTE;
+
+ private PGPUserAttributeSubpacketVector mVector;
+
+ WrappedUserAttribute(PGPUserAttributeSubpacketVector vector) {
+ mVector = vector;
+ }
+
+ PGPUserAttributeSubpacketVector getVector() {
+ return mVector;
+ }
+
+ public int getType() {
+ UserAttributeSubpacket[] subpackets = mVector.toSubpacketArray();
+ if (subpackets.length > 0) {
+ return subpackets[0].getType();
+ }
+ return 0;
+ }
+
+ public static WrappedUserAttribute fromSubpacket (int type, byte[] data) {
+ UserAttributeSubpacket subpacket = new UserAttributeSubpacket(type, data);
+ PGPUserAttributeSubpacketVector vector = new PGPUserAttributeSubpacketVector(
+ new UserAttributeSubpacket[] { subpacket });
+
+ return new WrappedUserAttribute(vector);
+
+ }
+
+ public byte[] getEncoded () throws IOException {
+ UserAttributeSubpacket[] subpackets = mVector.toSubpacketArray();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ for (UserAttributeSubpacket subpacket : subpackets) {
+ subpacket.encode(out);
+ }
+ return out.toByteArray();
+ }
+
+ public static WrappedUserAttribute fromData (byte[] data) throws IOException {
+ UserAttributeSubpacketInputStream in =
+ new UserAttributeSubpacketInputStream(new ByteArrayInputStream(data));
+ ArrayList<UserAttributeSubpacket> list = new ArrayList<UserAttributeSubpacket>();
+ while (in.available() > 0) {
+ list.add(in.readPacket());
+ }
+ UserAttributeSubpacket[] result = new UserAttributeSubpacket[list.size()];
+ list.toArray(result);
+ return new WrappedUserAttribute(
+ new PGPUserAttributeSubpacketVector(result));
+ }
+
+ /** Writes this object to an ObjectOutputStream. */
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ BCPGOutputStream bcpg = new BCPGOutputStream(baos);
+ bcpg.writePacket(new UserAttributePacket(mVector.toSubpacketArray()));
+ out.writeObject(baos.toByteArray());
+
+ }
+
+ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+
+ byte[] data = (byte[]) in.readObject();
+ BCPGInputStream bcpg = new BCPGInputStream(new ByteArrayInputStream(data));
+ Packet p = bcpg.readPacket();
+ if ( ! UserAttributePacket.class.isInstance(p)) {
+ throw new IOException("Could not decode UserAttributePacket!");
+ }
+ mVector = new PGPUserAttributeSubpacketVector(((UserAttributePacket) p).getSubpackets());
+
+ }
+
+ private void readObjectNoData() throws ObjectStreamException {
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!WrappedUserAttribute.class.isInstance(o)) {
+ return false;
+ }
+ return mVector.equals(((WrappedUserAttribute) o).mVector);
+ }
+
+}
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 6127002bb..5856589c4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -51,9 +51,11 @@ public class KeychainContract {
String EXPIRY = "expiry";
}
- interface UserIdsColumns {
+ interface UserPacketsColumns {
String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID
+ String TYPE = "type"; // not a database id
String USER_ID = "user_id"; // not a database id
+ String ATTRIBUTE_DATA = "attribute_data"; // not a database id
String RANK = "rank"; // ONLY used for sorting! no key, no nothing!
String IS_PRIMARY = "is_primary";
String IS_REVOKED = "is_revoked";
@@ -83,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
@@ -104,8 +111,9 @@ 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, UserIdsColumns {
+ public static class KeyRings implements BaseColumns, KeysColumns, UserPacketsColumns {
public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID;
public static final String IS_REVOKED = KeysColumns.IS_REVOKED;
public static final String VERIFIED = CertsColumns.VERIFIED;
@@ -225,7 +233,7 @@ public class KeychainContract {
}
- public static class UserIds implements UserIdsColumns, BaseColumns {
+ public static class UserPackets implements UserPacketsColumns, BaseColumns {
public static final String VERIFIED = "verified";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
@@ -303,10 +311,27 @@ 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 = UserIdsColumns.USER_ID;
+ public static final String USER_ID = UserPacketsColumns.USER_ID;
public static final String SIGNER_UID = "signer_user_id";
+ public static final int UNVERIFIED = 0;
public static final int VERIFIED_SECRET = 1;
public static final int VERIFIED_SELF = 2;
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 84a50dc65..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,11 +29,12 @@ 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;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity;
import org.sufficientlysecure.keychain.util.Log;
@@ -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 = 6;
+ private static final int DATABASE_VERSION = 8;
static Boolean apgHack = false;
private Context mContext;
@@ -60,10 +61,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
String KEY_RINGS_PUBLIC = "keyrings_public";
String KEY_RINGS_SECRET = "keyrings_secret";
String KEYS = "keys";
- String USER_IDS = "user_ids";
+ String USER_PACKETS = "user_ids";
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 =
@@ -106,18 +108,20 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ ")";
- private static final String CREATE_USER_IDS =
- "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + "("
- + UserIdsColumns.MASTER_KEY_ID + " INTEGER, "
- + UserIdsColumns.USER_ID + " TEXT, "
+ private static final String CREATE_USER_PACKETS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.USER_PACKETS + "("
+ + UserPacketsColumns.MASTER_KEY_ID + " INTEGER, "
+ + UserPacketsColumns.TYPE + " INT, "
+ + UserPacketsColumns.USER_ID + " TEXT, "
+ + UserPacketsColumns.ATTRIBUTE_DATA + " BLOB, "
- + UserIdsColumns.IS_PRIMARY + " INTEGER, "
- + UserIdsColumns.IS_REVOKED + " INTEGER, "
- + UserIdsColumns.RANK+ " INTEGER, "
+ + UserPacketsColumns.IS_PRIMARY + " INTEGER, "
+ + UserPacketsColumns.IS_REVOKED + " INTEGER, "
+ + UserPacketsColumns.RANK+ " INTEGER, "
- + "PRIMARY KEY(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.USER_ID + "), "
- + "UNIQUE (" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + "), "
- + "FOREIGN KEY(" + UserIdsColumns.MASTER_KEY_ID + ") REFERENCES "
+ + "PRIMARY KEY(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.USER_ID + "), "
+ + "UNIQUE (" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + "), "
+ + "FOREIGN KEY(" + UserPacketsColumns.MASTER_KEY_ID + ") REFERENCES "
+ Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ ")";
@@ -138,7 +142,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ") REFERENCES "
+ Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE,"
+ "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ") REFERENCES "
- + Tables.USER_IDS + "(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + ") ON DELETE CASCADE"
+ + Tables.USER_PACKETS + "(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + ") ON DELETE CASCADE"
+ ")";
private static final String CREATE_API_APPS =
@@ -164,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;
@@ -189,10 +205,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL(CREATE_KEYRINGS_PUBLIC);
db.execSQL(CREATE_KEYRINGS_SECRET);
db.execSQL(CREATE_KEYS);
- db.execSQL(CREATE_USER_IDS);
+ db.execSQL(CREATE_USER_PACKETS);
db.execSQL(CREATE_CERTS);
db.execSQL(CREATE_API_APPS);
db.execSQL(CREATE_API_APPS_ACCOUNTS);
+ db.execSQL(CREATE_API_APPS_ALLOWED_KEYS);
}
@Override
@@ -238,6 +255,18 @@ public class KeychainDatabase extends SQLiteOpenHelper {
case 5:
// do consolidate for 3.0 beta3
// fall through
+ 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 d40287690..2601c1f69 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -32,12 +32,14 @@ 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;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log;
@@ -62,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;
@@ -161,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);
@@ -171,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;
}
@@ -205,7 +213,7 @@ public class KeychainProvider extends ContentProvider {
return Keys.CONTENT_TYPE;
case KEY_RING_USER_IDS:
- return UserIds.CONTENT_TYPE;
+ return UserPackets.CONTENT_TYPE;
case KEY_RING_SECRET:
return KeyRings.CONTENT_ITEM_TYPE;
@@ -222,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);
}
@@ -247,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);
@@ -262,7 +273,7 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(KeyRings.EXPIRY, Tables.KEYS + "." + Keys.EXPIRY);
projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM);
projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT);
- projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
+ projectionMap.put(KeyRings.USER_ID, UserPackets.USER_ID);
projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED);
projectionMap.put(KeyRings.PUBKEY_DATA,
Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA
@@ -296,11 +307,12 @@ public class KeychainProvider extends ContentProvider {
qb.setTables(
Tables.KEYS
- + " INNER JOIN " + Tables.USER_IDS + " ON ("
+ + " INNER JOIN " + Tables.USER_PACKETS + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
- + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
- + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = 0"
+ + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ // we KNOW that the rank zero user packet is a user id!
+ + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = 0"
+ ") LEFT JOIN " + Tables.CERTS + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
@@ -376,7 +388,7 @@ public class KeychainProvider extends ContentProvider {
String subkey = Long.valueOf(uri.getLastPathSegment()).toString();
qb.appendWhere(" AND EXISTS ("
+ " SELECT 1 FROM " + Tables.KEYS + " AS tmp"
- + " WHERE tmp." + UserIds.MASTER_KEY_ID
+ + " WHERE tmp." + UserPackets.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND tmp." + Keys.KEY_ID + " = " + subkey + ""
+ ")");
@@ -398,15 +410,15 @@ public class KeychainProvider extends ContentProvider {
if (i != 0) {
emailWhere += " OR ";
}
- emailWhere += "tmp." + UserIds.USER_ID + " LIKE ";
+ emailWhere += "tmp." + UserPackets.USER_ID + " LIKE ";
// match '*<email>', so it has to be at the *end* of the user id
emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
gotCondition = true;
}
if(gotCondition) {
qb.appendWhere(" AND EXISTS ("
- + " SELECT 1 FROM " + Tables.USER_IDS + " AS tmp"
- + " WHERE tmp." + UserIds.MASTER_KEY_ID
+ + " SELECT 1 FROM " + Tables.USER_PACKETS + " AS tmp"
+ + " WHERE tmp." + UserPackets.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND (" + emailWhere + ")"
+ ")");
@@ -420,7 +432,7 @@ public class KeychainProvider extends ContentProvider {
}
if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIds.USER_ID + " ASC";
+ sortOrder = Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC";
}
// uri to watch is all /key_rings/
@@ -430,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);
@@ -458,37 +470,45 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_USER_IDS:
case KEY_RING_USER_IDS: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
- projectionMap.put(UserIds._ID, Tables.USER_IDS + ".oid AS _id");
- projectionMap.put(UserIds.MASTER_KEY_ID, Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID);
- projectionMap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
- projectionMap.put(UserIds.RANK, Tables.USER_IDS + "." + UserIds.RANK);
- projectionMap.put(UserIds.IS_PRIMARY, Tables.USER_IDS + "." + UserIds.IS_PRIMARY);
- projectionMap.put(UserIds.IS_REVOKED, Tables.USER_IDS + "." + UserIds.IS_REVOKED);
+ 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);
+ projectionMap.put(UserPackets.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID);
+ projectionMap.put(UserPackets.ATTRIBUTE_DATA, Tables.USER_PACKETS + "." + UserPackets.ATTRIBUTE_DATA);
+ projectionMap.put(UserPackets.RANK, Tables.USER_PACKETS + "." + UserPackets.RANK);
+ projectionMap.put(UserPackets.IS_PRIMARY, Tables.USER_PACKETS + "." + UserPackets.IS_PRIMARY);
+ projectionMap.put(UserPackets.IS_REVOKED, Tables.USER_PACKETS + "." + UserPackets.IS_REVOKED);
// we take the minimum (>0) here, where "1" is "verified by known secret key"
- projectionMap.put(UserIds.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserIds.VERIFIED);
+ projectionMap.put(UserPackets.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserPackets.VERIFIED);
qb.setProjectionMap(projectionMap);
- qb.setTables(Tables.USER_IDS
+ qb.setTables(Tables.USER_PACKETS
+ " LEFT JOIN " + Tables.CERTS + " ON ("
- + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = "
+ + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = "
+ Tables.CERTS + "." + Certs.MASTER_KEY_ID
- + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = "
+ + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = "
+ Tables.CERTS + "." + Certs.RANK
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0"
+ ")");
- groupBy = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
- + ", " + Tables.USER_IDS + "." + UserIds.RANK;
+ groupBy = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ + ", " + Tables.USER_PACKETS + "." + UserPackets.RANK;
+
+ // for now, we only respect user ids here, so TYPE must be NULL
+ // TODO expand with KEY_RING_USER_PACKETS query type which lifts this restriction
+ qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL");
// If we are searching for a particular keyring's ids, add where
if (match == KEY_RING_USER_IDS) {
- qb.appendWhere(Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = ");
+ // TODO remove with the thing above
+ qb.appendWhere(" AND ");
+ qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
}
if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " ASC"
- + "," + Tables.USER_IDS + "." + UserIds.RANK + " ASC";
+ sortOrder = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " ASC"
+ + "," + Tables.USER_PACKETS + "." + UserPackets.RANK + " ASC";
}
break;
@@ -497,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);
@@ -515,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);
@@ -533,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);
@@ -542,20 +562,24 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
projectionMap.put(Certs.DATA, Tables.CERTS + "." + Certs.DATA);
- projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
- projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID);
+ projectionMap.put(Certs.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID);
+ projectionMap.put(Certs.SIGNER_UID, "signer." + UserPackets.USER_ID + " AS " + Certs.SIGNER_UID);
qb.setProjectionMap(projectionMap);
qb.setTables(Tables.CERTS
- + " JOIN " + Tables.USER_IDS + " ON ("
+ + " JOIN " + Tables.USER_PACKETS + " ON ("
+ Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = "
- + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
+ + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ " AND "
+ Tables.CERTS + "." + Certs.RANK + " = "
- + Tables.USER_IDS + "." + UserIds.RANK
- + ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON ("
+ + Tables.USER_PACKETS + "." + UserPackets.RANK
+ // for now, we only return user ids here, so TYPE must be NULL
+ // TODO at some point, we should lift this restriction
+ + " AND "
+ + Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL"
+ + ") LEFT JOIN " + Tables.USER_PACKETS + " AS signer ON ("
+ Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = "
- + "signer." + UserIds.MASTER_KEY_ID
+ + "signer." + UserPackets.MASTER_KEY_ID
+ " AND "
+ "signer." + Keys.RANK + " = 0"
+ ")");
@@ -600,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 + ")");
@@ -662,8 +692,18 @@ public class KeychainProvider extends ContentProvider {
break;
case KEY_RING_USER_IDS:
- db.insertOrThrow(Tables.USER_IDS, null, values);
- keyId = values.getAsLong(UserIds.MASTER_KEY_ID);
+ // iff TYPE is null, user_id MUST be null as well
+ if ( ! (values.get(UserPacketsColumns.TYPE) == null
+ ? (values.get(UserPacketsColumns.USER_ID) != null && values.get(UserPacketsColumns.ATTRIBUTE_DATA) == null)
+ : (values.get(UserPacketsColumns.ATTRIBUTE_DATA) != null && values.get(UserPacketsColumns.USER_ID) == null)
+ )) {
+ throw new AssertionError("Incorrect type for user packet! This is a bug!");
+ }
+ if (((Number)values.get(UserPacketsColumns.RANK)).intValue() == 0 && values.get(UserPacketsColumns.USER_ID) == null) {
+ throw new AssertionError("Rank 0 user packet must be a user id!");
+ }
+ db.insertOrThrow(Tables.USER_PACKETS, null, values);
+ keyId = values.getAsLong(UserPackets.MASTER_KEY_ID);
break;
case KEY_RING_CERTS:
@@ -677,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);
@@ -685,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;
}
@@ -753,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);
}
@@ -845,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 05dc99c5e..d947ae053 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -30,11 +30,13 @@ import android.support.v4.util.LongSparseArray;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-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;
@@ -42,27 +44,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.UserIds;
+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;
@@ -169,7 +171,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) {
@@ -192,6 +194,9 @@ public class ProviderHelper {
}
pos += 1;
}
+ } else {
+ // If no data was found, throw an appropriate exception
+ throw new NotFoundException();
}
return result;
@@ -217,7 +222,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);
@@ -346,7 +351,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
@@ -436,18 +441,18 @@ 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<>();
+
if (trustedKeys.size() == 0) {
log(LogType.MSG_IP_UID_CLASSIFYING_ZERO);
} else {
log(LogType.MSG_IP_UID_CLASSIFYING, trustedKeys.size());
}
mIndent += 1;
- List<UserIdItem> uids = new ArrayList<UserIdItem>();
- for (byte[] rawUserId : new IterableIterator<byte[]>(
- masterKey.getUnorderedRawUserIds().iterator())) {
+ for (byte[] rawUserId : masterKey.getUnorderedRawUserIds()) {
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
- UserIdItem item = new UserIdItem();
+ UserPacketItem item = new UserPacketItem();
uids.add(item);
item.userId = userId;
@@ -456,7 +461,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
@@ -468,7 +473,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;
@@ -530,6 +535,106 @@ public class ProviderHelper {
}
mIndent -= 1;
+ ArrayList<WrappedUserAttribute> userAttributes = masterKey.getUnorderedUserAttributes();
+ // Don't spam the log if there aren't even any attributes
+ if ( ! userAttributes.isEmpty()) {
+ log(LogType.MSG_IP_UAT_CLASSIFYING);
+ }
+
+ mIndent += 1;
+ for (WrappedUserAttribute userAttribute : userAttributes) {
+
+ UserPacketItem item = new UserPacketItem();
+ uids.add(item);
+ item.type = userAttribute.getType();
+ item.attributeData = userAttribute.getEncoded();
+
+ int unknownCerts = 0;
+
+ switch (item.type) {
+ case WrappedUserAttribute.UAT_IMAGE:
+ log(LogType.MSG_IP_UAT_PROCESSING_IMAGE);
+ break;
+ default:
+ log(LogType.MSG_IP_UAT_PROCESSING_UNKNOWN);
+ break;
+ }
+ mIndent += 1;
+ // look through signatures for this specific key
+ for (WrappedSignature cert : new IterableIterator<>(
+ masterKey.getSignaturesForUserAttribute(userAttribute))) {
+ long certId = cert.getKeyId();
+ // self signature
+ if (certId == masterKeyId) {
+
+ // 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.selfRevocation = cert;
+ log(LogType.MSG_IP_UAT_REVOKED);
+ }
+ continue;
+
+ }
+
+ // do we have a trusted key for this?
+ if (trustedKeys.indexOfKey(certId) < 0) {
+ unknownCerts += 1;
+ continue;
+ }
+
+ // verify signatures from known private keys
+ CanonicalizedPublicKey trustedKey = trustedKeys.get(certId);
+
+ try {
+ cert.init(trustedKey);
+ // if it doesn't certify, leave a note and skip
+ if ( ! cert.verifySignature(masterKey, userAttribute)) {
+ log(LogType.MSG_IP_UAT_CERT_BAD);
+ continue;
+ }
+
+ log(cert.isRevocation()
+ ? LogType.MSG_IP_UAT_CERT_GOOD_REVOKE
+ : LogType.MSG_IP_UAT_CERT_GOOD,
+ KeyFormattingUtils.convertKeyIdToHexShort(trustedKey.getKeyId())
+ );
+
+ // check if there is a previous certificate
+ WrappedSignature prev = item.trustedCerts.get(cert.getKeyId());
+ if (prev != null) {
+ // if it's newer, skip this one
+ if (prev.getCreationTime().after(cert.getCreationTime())) {
+ log(LogType.MSG_IP_UAT_CERT_OLD);
+ continue;
+ }
+ // if the previous one was a non-revokable certification, no need to look further
+ if (!prev.isRevocation() && !prev.isRevokable()) {
+ log(LogType.MSG_IP_UAT_CERT_NONREVOKE);
+ continue;
+ }
+ log(LogType.MSG_IP_UAT_CERT_NEW);
+ }
+ item.trustedCerts.put(cert.getKeyId(), cert);
+
+ } catch (PgpGeneralException e) {
+ log(LogType.MSG_IP_UAT_CERT_ERROR,
+ KeyFormattingUtils.convertKeyIdToHex(cert.getKeyId()));
+ }
+
+ }
+
+ if (unknownCerts > 0) {
+ log(LogType.MSG_IP_UAT_CERTS_UNKNOWN, unknownCerts);
+ }
+ mIndent -= 1;
+
+ }
+ mIndent -= 1;
+
progress.setProgress(LogType.MSG_IP_UID_REORDER.getMsgId(), 65, 100);
log(LogType.MSG_IP_UID_REORDER);
// primary before regular before revoked (see UserIdItem.compareTo)
@@ -537,17 +642,23 @@ public class ProviderHelper {
Collections.sort(uids);
// iterate and put into db
for (int userIdRank = 0; userIdRank < uids.size(); userIdRank++) {
- UserIdItem item = uids.get(userIdRank);
+ UserPacketItem item = uids.get(userIdRank);
operations.add(buildUserIdOperations(masterKeyId, item, userIdRank));
- if (item.selfCert != null) {
- 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);
@@ -601,23 +712,32 @@ public class ProviderHelper {
}
- private static class UserIdItem implements Comparable<UserIdItem> {
+ private static class UserPacketItem implements Comparable<UserPacketItem> {
+ Integer type;
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(UserIdItem o) {
+ 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
+ if (type != o.type) {
+ return type == null ? -1 : 1;
+ }
// if one key is primary but the other isn't, the primary one always comes first
if (isPrimary != o.isPrimary) {
return isPrimary ? -1 : 1;
}
- // revoked keys always come last!
- if (isRevoked != o.isRevoked) {
- return isRevoked ? 1 : -1;
- }
return 0;
}
}
@@ -682,6 +802,11 @@ public class ProviderHelper {
KeyFormattingUtils.convertKeyIdToHex(id)
);
break;
+ case PIN:
+ log(LogType.MSG_IS_SUBKEY_PIN,
+ KeyFormattingUtils.convertKeyIdToHex(id)
+ );
+ break;
case GNU_DUMMY:
log(LogType.MSG_IS_SUBKEY_STRIPPED,
KeyFormattingUtils.convertKeyIdToHex(id)
@@ -936,6 +1061,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!
@@ -958,7 +1088,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;
@@ -1020,7 +1150,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;
@@ -1102,13 +1232,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();
@@ -1136,6 +1264,7 @@ public class ProviderHelper {
try {
+ cachePublic = new ParcelableFileCache<>(mContext, "consolidate_public.pcl");
IteratorWithSize<ParcelableKeyRing> itPublics = cachePublic.readCache();
int numPublics = itPublics.getSize();
@@ -1191,7 +1320,6 @@ public class ProviderHelper {
progress.setProgress(100, 100);
log.add(LogType.MSG_CON_SUCCESS, indent);
- indent -= 1;
return new ConsolidateResult(ConsolidateResult.RESULT_OK, log);
@@ -1225,15 +1353,17 @@ public class ProviderHelper {
* Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing
*/
private ContentProviderOperation
- buildUserIdOperations(long masterKeyId, UserIdItem item, int rank) {
+ buildUserIdOperations(long masterKeyId, UserPacketItem item, int rank) {
ContentValues values = new ContentValues();
- values.put(UserIds.MASTER_KEY_ID, masterKeyId);
- values.put(UserIds.USER_ID, item.userId);
- values.put(UserIds.IS_PRIMARY, item.isPrimary);
- values.put(UserIds.IS_REVOKED, item.isRevoked);
- values.put(UserIds.RANK, rank);
+ values.put(UserPackets.MASTER_KEY_ID, masterKeyId);
+ values.put(UserPackets.TYPE, item.type);
+ 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.selfRevocation != null);
+ values.put(UserPackets.RANK, rank);
- Uri uri = UserIds.buildUserIdsUri(masterKeyId);
+ Uri uri = UserPackets.buildUserIdsUri(masterKeyId);
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
@@ -1261,7 +1391,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);
@@ -1366,7 +1496,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 {
@@ -1385,8 +1515,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 dc9592710..d95701458 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -38,7 +38,10 @@ 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;
@@ -47,30 +50,20 @@ import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-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.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
-import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
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 org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
-import org.sufficientlysecure.keychain.util.Preferences;
-import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -79,8 +72,6 @@ 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;
@@ -113,6 +104,8 @@ public class KeychainIntentService extends IntentService implements Progressable
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";
@@ -135,24 +128,11 @@ public class KeychainIntentService extends IntentService implements Progressable
// 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";
@@ -188,6 +168,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";
@@ -196,9 +180,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";
@@ -251,461 +232,341 @@ public class KeychainIntentService extends IntentService implements Progressable
String action = intent.getAction();
// executeServiceMethod action from extra bundle
- if (ACTION_CERTIFY_KEYRING.equals(action)) {
-
- // Input
- CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL);
- String keyServerUri = data.getString(UPLOAD_KEY_SERVER);
+ switch (action) {
+ case ACTION_CERTIFY_KEYRING: {
- // 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();
-
- // 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);
+ Bundle resultData = new Bundle();
- DecryptVerifyResult decryptVerifyResult = builder.build().execute();
+ // 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);
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, decryptVerifyResult);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
+ DecryptVerifyResult decryptVerifyResult = builder.build().execute();
- } else if (ACTION_VERIFY_KEYBASE_PROOF.equals(action)) {
- try {
- Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF)));
- setProgress(R.string.keybase_message_fetching_data, 0, 100);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, decryptVerifyResult);
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
- Prover prover = Prover.findProverFor(proof);
+ break;
+ }
+ case ACTION_VERIFY_KEYBASE_PROOF: {
- if (prover == null) {
- sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof.getPrettyName());
- return;
- }
+ try {
+ Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF)));
+ setProgress(R.string.keybase_message_fetching_data, 0, 100);
- 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;
- }
+ Prover prover = Prover.findProverFor(proof);
- 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));
+ if (prover == null) {
+ sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof.getPrettyName());
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.fetchProofData()) {
+ sendProofError(prover.getLog(), getString(R.string.keybase_problem_fetching_evidence));
+ return;
}
- if (!prover.checkDnsTxt(extents)) {
- sendProofError(prover.getLog(), null);
+ String requiredFingerprint = data.getString(KEYBASE_REQUIRED_FINGERPRINT);
+ if (!prover.checkFingerprint(requiredFingerprint)) {
+ sendProofError(getString(R.string.keybase_key_mismatch));
return;
}
- }
- byte[] messageBytes = prover.getPgpMessage().getBytes();
- if (prover.rawMessageCheckRequired()) {
- InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(messageBytes));
- if (!prover.checkRawMessageBytes(messageByteStream)) {
- sendProofError(prover.getLog(), null);
- return;
+ 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;
+ }
}
- }
- // 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);
+ byte[] messageBytes = prover.getPgpMessage().getBytes();
+ if (prover.rawMessageCheckRequired()) {
+ InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(messageBytes));
+ if (!prover.checkRawMessageBytes(messageByteStream)) {
+ sendProofError(prover.getLog(), null);
+ return;
+ }
+ }
+
+ // 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);
- InputData inputData = createDecryptInputData(data);
- OutputStream outStream = createCryptOutputStream(data);
+ InputData inputData = createDecryptInputData(data);
+ OutputStream outStream = createCryptOutputStream(data);
+
+ PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
+ this, new ProviderHelper(this), this,
+ inputData, outStream
+ );
+ builder.setSignedLiteralData(true).setRequiredSignerFingerprint(requiredFingerprint);
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(this), this,
- inputData, outStream
- );
- builder.setSignedLiteralData(true).setRequiredSignerFingerprint(requiredFingerprint);
+ DecryptVerifyResult decryptVerifyResult = builder.build().execute();
+ outStream.close();
- DecryptVerifyResult decryptVerifyResult = builder.build().execute();
- outStream.close();
+ if (!decryptVerifyResult.success()) {
+ OperationLog log = decryptVerifyResult.getLog();
+ OperationResult.LogEntryParcel lastEntry = null;
+ for (OperationResult.LogEntryParcel entry : log) {
+ lastEntry = entry;
+ }
+ sendProofError(getString(lastEntry.mType.getMsgId()));
+ return;
+ }
- if (!decryptVerifyResult.success()) {
- OperationLog log = decryptVerifyResult.getLog();
- OperationResult.LogEntryParcel lastEntry = null;
- for (OperationResult.LogEntryParcel entry : log) {
- lastEntry = entry;
+ if (!prover.validate(outStream.toString())) {
+ sendProofError(getString(R.string.keybase_message_payload_mismatch));
+ return;
}
- sendProofError(getString(lastEntry.mType.getMsgId()));
- return;
- }
- if (!prover.validate(outStream.toString())) {
- sendProofError(getString(R.string.keybase_message_payload_mismatch));
- return;
- }
+ Bundle resultData = new Bundle();
+ resultData.putString(KeychainIntentServiceHandler.DATA_MESSAGE, "OK");
- Bundle resultData = new Bundle();
- resultData.putString(KeychainIntentServiceHandler.DATA_MESSAGE, "OK");
+ // 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);
+ }
- // 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);
+ break;
}
+ case ACTION_DECRYPT_VERIFY: {
- } else if (ACTION_DECRYPT_VERIFY.equals(action)) {
-
- try {
+ 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);
- OutputStream outStream = createCryptOutputStream(data);
+ InputData inputData = createDecryptInputData(data);
+ OutputStream outStream = createCryptOutputStream(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, outStream
- );
- builder.setAllowSymmetricDecryption(true)
- .setPassphrase(passphrase)
- .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, outStream
+ );
+ builder.setAllowSymmetricDecryption(true)
+ .setPassphrase(passphrase)
+ .setNfcState(nfcDecryptedSessionKey);
- DecryptVerifyResult decryptVerifyResult = builder.build().execute();
+ DecryptVerifyResult decryptVerifyResult = builder.build().execute();
- outStream.close();
+ outStream.close();
- resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult);
+ resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult);
/* Output */
- finalizeDecryptOutputStream(data, resultData, outStream);
+ finalizeDecryptOutputStream(data, resultData, outStream);
- Log.logDebugBundle(resultData, "resultData");
+ Log.logDebugBundle(resultData, "resultData");
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
- } else if (ACTION_DELETE.equals(action)) {
+ break;
+ }
+ case ACTION_DELETE: {
- // Input
- long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
- boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
+ // Input
+ long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
+ boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
- // Operation
- DeleteOperation op = new DeleteOperation(this, new ProviderHelper(this), this);
- DeleteResult result = op.execute(masterKeyIds, isSecret);
+ // Operation
+ DeleteOperation op = new DeleteOperation(this, new ProviderHelper(this), this);
+ DeleteResult result = op.execute(masterKeyIds, isSecret);
- // Result
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- } else if (ACTION_EDIT_KEYRING.equals(action)) {
+ break;
+ }
+ case ACTION_EDIT_KEYRING: {
- try {
- /* Input */
+ // Input
SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
- if (saveParcel == null) {
- Log.e(Constants.TAG, "bug: missing save_keyring_parcel in data!");
- return;
- }
-
- /* Operation */
- PgpKeyOperation keyOperations =
- new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100), mActionCanceled);
- EditKeyResult modifyResult;
-
- if (saveParcel.mMasterKeyId != null) {
- String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE);
- CanonicalizedSecretKeyRing secRing =
- new ProviderHelper(this).getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId);
-
- modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase);
- } else {
- modifyResult = keyOperations.createSecretKeyRing(saveParcel);
- }
-
- // If the edit operation didn't succeed, exit here
- if (!modifyResult.success()) {
- // always return SaveKeyringResult, so create one out of the EditKeyResult
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, modifyResult);
- return;
- }
-
- UncachedKeyRing ring = modifyResult.getRing();
+ String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE);
- // Check if the action was cancelled
- if (mActionCanceled.get()) {
- OperationLog log = modifyResult.getLog();
- // If it wasn't added before, add log entry
- if (!modifyResult.cancelled()) {
- log.add(LogType.MSG_OPERATION_CANCELLED, 0);
- }
- // If so, just stop without saving
- modifyResult = new EditKeyResult(
- EditKeyResult.RESULT_CANCELLED, log, null);
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, modifyResult);
- return;
- }
+ // Operation
+ EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled);
+ EditKeyResult result = op.execute(saveParcel, passphrase);
- // Save the keyring. The ProviderHelper is initialized with the previous log
- SaveKeyringResult saveResult = new ProviderHelper(this, modifyResult.getLog())
- .saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100));
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- // If the edit operation didn't succeed, exit here
- if (!saveResult.success()) {
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult);
- return;
- }
+ break;
+ }
+ case ACTION_PROMOTE_KEYRING: {
- // cache new passphrase
- if (saveParcel.mNewPassphrase != null) {
- PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(), ring.getMasterKeyId(),
- saveParcel.mNewPassphrase, ring.getPublicKey().getPrimaryUserIdWithFallback());
- }
+ // Input
+ long keyRingId = data.getInt(EXPORT_KEY_RING_MASTER_KEY_ID);
- setProgress(R.string.progress_done, 100, 100);
+ // Operation
+ PromoteKeyOperation op = new PromoteKeyOperation(this, providerHelper, this, mActionCanceled);
+ PromoteKeyResult result = op.execute(keyRingId);
- // make sure new data is synced into contacts
- ContactSyncAdapterService.requestSync();
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- /* Output */
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult);
- } catch (Exception e) {
- sendErrorToHandler(e);
+ break;
}
+ case ACTION_EXPORT_KEYRING: {
- } else if (ACTION_EXPORT_KEYRING.equals(action)) {
+ // Input
+ boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
+ String outputFile = data.getString(EXPORT_FILENAME);
+ Uri outputUri = data.getParcelable(EXPORT_URI);
- // Input
- boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
- String outputFile = data.getString(EXPORT_FILENAME);
- Uri outputUri = data.getParcelable(EXPORT_URI);
+ boolean exportAll = data.getBoolean(EXPORT_ALL);
+ long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
- boolean exportAll = data.getBoolean(EXPORT_ALL);
- long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
+ // 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);
+ }
+ // Result
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- // 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);
+ break;
}
-
- // Result
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
-
- } else if (ACTION_IMPORT_KEYRING.equals(action)) {
-
- try {
+ case ACTION_IMPORT_KEYRING: {
// 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();
- }
+ ArrayList<ParcelableKeyRing> list = data.getParcelableArrayList(IMPORT_KEY_LIST);
+ ParcelableFileCache<ParcelableKeyRing> cache =
+ new ParcelableFileCache<>(this, "key_import.pcl");
// Operation
ImportExportOperation importExportOperation = new ImportExportOperation(
this, providerHelper, this, mActionCanceled);
- ImportKeyResult result = importExportOperation.importKeyRings(entries, numEntries, keyServer);
-
- // Special: consolidate on secret key import (cannot be cancelled!)
- if (result.mSecret > 0) {
- // TODO move this into the import operation
- providerHelper.consolidateDatabaseStep1(this);
- }
-
- // Special: make sure new data is synced into contacts
- ContactSyncAdapterService.requestSync();
+ // Either list or cache must be null, no guarantees otherwise.
+ ImportKeyResult result = list != null
+ ? importExportOperation.importKeyRings(list, keyServer)
+ : importExportOperation.importKeyRings(cache, keyServer);
// Result
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
- } else if (ACTION_SIGN_ENCRYPT.equals(action)) {
-
- try {
- /* Input */
- int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
- Bundle resultData = new Bundle();
-
- long sigMasterKeyId = data.getLong(ENCRYPT_SIGNATURE_MASTER_ID);
- String sigKeyPassphrase = data.getString(ENCRYPT_SIGNATURE_KEY_PASSPHRASE);
-
- byte[] nfcHash = data.getByteArray(ENCRYPT_SIGNATURE_NFC_HASH);
- Date nfcTimestamp = (Date) data.getSerializable(ENCRYPT_SIGNATURE_NFC_TIMESTAMP);
-
- String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE);
-
- 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);
-
- /* 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);
-
- try {
-
- // 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);
- }
-
- } catch (PgpKeyNotFoundException e) {
- // encrypt-only
- // TODO Just silently drop the requested signature? Shouldn't we throw here?
- }
-
- // this assumes that the bytes are cleartext (valid for current implementation!)
- if (source == IO_BYTES) {
- builder.setCleartextInput(true);
- }
-
- SignEncryptResult result = builder.build().execute();
- resultData.putParcelable(SignEncryptResult.EXTRA_RESULT, 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) {
@@ -810,10 +671,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) {
@@ -827,33 +684,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!");
}
@@ -870,20 +700,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);
}
@@ -896,7 +717,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 fc65757f5..ceb0a2d2b 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 {
@@ -136,6 +136,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 c4be467e4..e2d0c03c9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -21,6 +21,8 @@ package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
+import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
+
import java.io.Serializable;
import java.util.ArrayList;
@@ -46,9 +48,10 @@ public class SaveKeyringParcel implements Parcelable {
// the key fingerprint, for safety. MUST be null for a new key.
public byte[] mFingerprint;
- public String mNewPassphrase;
+ public ChangeUnlockParcel mNewUnlock;
public ArrayList<String> mAddUserIds;
+ public ArrayList<WrappedUserAttribute> mAddUserAttribute;
public ArrayList<SubkeyAdd> mAddSubKeys;
public ArrayList<SubkeyChange> mChangeSubKeys;
@@ -56,7 +59,6 @@ public class SaveKeyringParcel implements Parcelable {
public ArrayList<String> mRevokeUserIds;
public ArrayList<Long> mRevokeSubKeys;
- public ArrayList<Long> mStripSubKeys;
public SaveKeyringParcel() {
reset();
@@ -69,14 +71,31 @@ public class SaveKeyringParcel implements Parcelable {
}
public void reset() {
- mNewPassphrase = null;
- mAddUserIds = new ArrayList<String>();
- mAddSubKeys = new ArrayList<SubkeyAdd>();
+ mNewUnlock = null;
+ 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,
@@ -109,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;
}
@@ -159,9 +205,10 @@ public class SaveKeyringParcel implements Parcelable {
mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;
mFingerprint = source.createByteArray();
- mNewPassphrase = source.readString();
+ mNewUnlock = source.readParcelable(getClass().getClassLoader());
mAddUserIds = source.createStringArrayList();
+ mAddUserAttribute = (ArrayList<WrappedUserAttribute>) source.readSerializable();
mAddSubKeys = (ArrayList<SubkeyAdd>) source.readSerializable();
mChangeSubKeys = (ArrayList<SubkeyChange>) source.readSerializable();
@@ -169,7 +216,6 @@ public class SaveKeyringParcel implements Parcelable {
mRevokeUserIds = source.createStringArrayList();
mRevokeSubKeys = (ArrayList<Long>) source.readSerializable();
- mStripSubKeys = (ArrayList<Long>) source.readSerializable();
}
@Override
@@ -180,9 +226,11 @@ public class SaveKeyringParcel implements Parcelable {
}
destination.writeByteArray(mFingerprint);
- destination.writeString(mNewPassphrase);
+ // yes, null values are ok for parcelables
+ destination.writeParcelable(mNewUnlock, 0);
destination.writeStringList(mAddUserIds);
+ destination.writeSerializable(mAddUserAttribute);
destination.writeSerializable(mAddSubKeys);
destination.writeSerializable(mChangeSubKeys);
@@ -190,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>() {
@@ -211,14 +258,14 @@ public class SaveKeyringParcel implements Parcelable {
@Override
public String toString() {
String out = "mMasterKeyId: " + mMasterKeyId + "\n";
- out += "mNewPassphrase: " + mNewPassphrase + "\n";
+ out += "mNewUnlock: " + mNewUnlock + "\n";
out += "mAddUserIds: " + mAddUserIds + "\n";
+ out += "mAddUserAttribute: " + mAddUserAttribute + "\n";
out += "mAddSubKeys: " + mAddSubKeys + "\n";
out += "mChangeSubKeys: " + mChangeSubKeys + "\n";
out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n";
out += "mRevokeUserIds: " + mRevokeUserIds + "\n";
- out += "mRevokeSubKeys: " + mRevokeSubKeys + "\n";
- out += "mStripSubKeys: " + mStripSubKeys;
+ out += "mRevokeSubKeys: " + mRevokeSubKeys;
return out;
}
@@ -238,4 +285,67 @@ public class SaveKeyringParcel implements Parcelable {
// BRAINPOOL_P256, BRAINPOOL_P384, BRAINPOOL_P512
}
+ /** This subclass contains information on how the passphrase should be changed.
+ *
+ * If no changes are to be made, this class should NOT be used!
+ *
+ * At this point, there must be *exactly one* non-null value here, which specifies the type
+ * of unlocking mechanism to use.
+ *
+ */
+ public static class ChangeUnlockParcel implements Parcelable {
+
+ // The new passphrase to use
+ public final String mNewPassphrase;
+ // A new pin to use. Must only contain [0-9]+
+ public final String mNewPin;
+
+ public ChangeUnlockParcel(String newPassphrase) {
+ this(newPassphrase, null);
+ }
+ public ChangeUnlockParcel(String newPassphrase, String newPin) {
+ if (newPassphrase == null && newPin == null) {
+ throw new RuntimeException("Cannot set both passphrase and pin. THIS IS A BUG!");
+ }
+ if (newPin != null && !newPin.matches("[0-9]+")) {
+ throw new RuntimeException("Pin must be numeric digits only. THIS IS A BUG!");
+ }
+ mNewPassphrase = newPassphrase;
+ mNewPin = newPin;
+ }
+
+ public ChangeUnlockParcel(Parcel source) {
+ mNewPassphrase = source.readString();
+ mNewPin = source.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeString(mNewPassphrase);
+ destination.writeString(mNewPin);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ChangeUnlockParcel> CREATOR = new Creator<ChangeUnlockParcel>() {
+ public ChangeUnlockParcel createFromParcel(final Parcel source) {
+ return new ChangeUnlockParcel(source);
+ }
+
+ public ChangeUnlockParcel[] newArray(final int size) {
+ return new ChangeUnlockParcel[size];
+ }
+ };
+
+ public String toString() {
+ return mNewPassphrase != null
+ ? ("passphrase (" + mNewPassphrase + ")")
+ : ("pin (" + mNewPin + ")");
+ }
+
+ }
+
}
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..777288e69 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
@@ -18,57 +17,47 @@
package org.sufficientlysecure.keychain.ui;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
import android.view.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.operations.results.OperationResult;
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,15 +68,25 @@ 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();
}
+ @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/CertifyFingerprintFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java
new file mode 100644
index 000000000..aef705ee9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java
@@ -0,0 +1,184 @@
+/*
+ * 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> {
+
+ 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.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{keyId});
+ startActivityForResult(certifyIntent, 0);
+ }
+
+}
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 4d10d8639..50d5e3229 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
@@ -49,7 +49,7 @@ 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.CachedPublicKeyRing;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
@@ -82,11 +82,11 @@ public class CertifyKeyFragment extends LoaderFragment
private long mSignMasterKeyId = Constants.key.none;
public static final String[] USER_IDS_PROJECTION = new String[]{
- UserIds._ID,
- UserIds.MASTER_KEY_ID,
- UserIds.USER_ID,
- UserIds.IS_PRIMARY,
- UserIds.IS_REVOKED
+ UserPackets._ID,
+ UserPackets.MASTER_KEY_ID,
+ UserPackets.USER_ID,
+ UserPackets.IS_PRIMARY,
+ UserPackets.IS_REVOKED
};
private static final int INDEX_MASTER_KEY_ID = 1;
private static final int INDEX_USER_ID = 2;
@@ -182,7 +182,7 @@ public class CertifyKeyFragment extends LoaderFragment
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- Uri uri = UserIds.buildUserIdsUri();
+ Uri uri = UserPackets.buildUserIdsUri();
String selection, ids[];
{
@@ -196,15 +196,15 @@ public class CertifyKeyFragment extends LoaderFragment
}
}
// put together selection string
- selection = UserIds.IS_REVOKED + " = 0" + " AND "
- + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
+ selection = UserPackets.IS_REVOKED + " = 0" + " AND "
+ + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ " IN (" + placeholders + ")";
}
return new CursorLoader(getActivity(), uri,
USER_IDS_PROJECTION, selection, ids,
- Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " ASC"
- + ", " + Tables.USER_IDS + "." + UserIds.USER_ID + " ASC"
+ Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " ASC"
+ + ", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC"
);
}
@@ -234,7 +234,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;
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 bcc98a5d7..6e0115342 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -34,16 +34,17 @@ import android.widget.TextView;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+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 {
@@ -165,7 +166,9 @@ public class CreateKeyFinalFragment extends Fragment {
String userId = KeyRing.createUserId(mName, mEmail, null);
mSaveKeyringParcel.mAddUserIds.add(userId);
mSaveKeyringParcel.mChangePrimaryUserId = userId;
- mSaveKeyringParcel.mNewPassphrase = mPassphrase;
+ mSaveKeyringParcel.mNewUnlock = mPassphrase != null
+ ? new ChangeUnlockParcel(mPassphrase, null)
+ : null;
}
}
@@ -187,14 +190,14 @@ public class CreateKeyFinalFragment extends Fragment {
if (returnData == null) {
return;
}
- final SaveKeyringResult result =
+ final EditKeyResult result =
returnData.getParcelable(OperationResult.EXTRA_RESULT);
if (result == null) {
Log.e(Constants.TAG, "result == null");
return;
}
- if (mUploadCheckbox.isChecked()) {
+ if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
// result will be displayed after upload
uploadKey(result);
} else {
@@ -224,7 +227,8 @@ public class CreateKeyFinalFragment extends Fragment {
getActivity().startService(intent);
}
- private void uploadKey(final SaveKeyringResult saveKeyResult) {
+ // TODO move into EditKeyOperation
+ private void uploadKey(final EditKeyResult saveKeyResult) {
// Send all information needed to service to upload key in other thread
final Intent intent = new Intent(getActivity(), KeychainIntentService.class);
@@ -232,7 +236,7 @@ public class CreateKeyFinalFragment extends Fragment {
// set data uri as path to keyring
Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
- saveKeyResult.mRingMasterKeyId);
+ saveKeyResult.mMasterKeyId);
intent.setData(blobUri);
// fill values for this action
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 a18eb76d1..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;
@@ -47,12 +46,15 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
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;
@@ -63,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;
@@ -145,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) {
@@ -159,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);
@@ -189,7 +185,9 @@ public class EditKeyFragment extends LoaderFragment implements
private void loadSaveKeyringParcel(SaveKeyringParcel saveKeyringParcel) {
mSaveKeyringParcel = saveKeyringParcel;
mPrimaryUserId = saveKeyringParcel.mChangePrimaryUserId;
- mCurrentPassphrase = saveKeyringParcel.mNewPassphrase;
+ if (saveKeyringParcel.mNewUnlock != null) {
+ mCurrentPassphrase = saveKeyringParcel.mNewUnlock.mNewPassphrase;
+ }
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds, true);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
@@ -226,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;
}
@@ -331,7 +326,7 @@ public class EditKeyFragment extends LoaderFragment implements
switch (id) {
case LOADER_ID_USER_IDS: {
- Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri);
+ Uri baseUri = UserPackets.buildUserIdsUri(mDataUri);
return new CursorLoader(getActivity(), baseUri,
UserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
}
@@ -379,6 +374,9 @@ public class EditKeyFragment extends LoaderFragment implements
}
private void changePassphrase() {
+// Intent passIntent = new Intent(getActivity(), PassphraseWizardActivity.class);
+// passIntent.setAction(PassphraseWizardActivity.CREATE_METHOD);
+// startActivityForResult(passIntent, 12);
// Message is received after passphrase is cached
Handler returnHandler = new Handler() {
@Override
@@ -387,8 +385,10 @@ public class EditKeyFragment extends LoaderFragment implements
Bundle data = message.getData();
// cache new returned passphrase!
- mSaveKeyringParcel.mNewPassphrase = data
- .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE);
+ mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(
+ data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
+ null
+ );
}
}
};
@@ -469,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 ed29fc909..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;
@@ -39,11 +38,9 @@ public class FirstTimeActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.first_time_activity);
+ super.onCreate(savedInstanceState);
mCreateKey = findViewById(R.id.first_time_create_key);
mImportKey = findViewById(R.id.first_time_import_key);
@@ -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,12 +92,12 @@ 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);
}
- startActivityForResult(intent, 0);
+ startActivity(intent);
finish();
}
@@ -105,4 +106,5 @@ public class FirstTimeActivity extends ActionBarActivity {
public boolean onKeyDown(int keyCode, KeyEvent event) {
return keyCode == KeyEvent.KEYCODE_MENU || super.onKeyDown(keyCode, event);
}
+
}
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..6638c9944 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -29,7 +29,6 @@ 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 +50,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 =
@@ -90,8 +89,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 +100,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 +115,123 @@ 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;
+ }
+ case ACTION_IMPORT_KEY_FROM_NFC: {
+ // NOTE: this only displays the appropriate fragment, no actions are taken
+ startFileFragment(savedInstanceState);
+ // TODO!!!!!
+
+ // 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 +373,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 +405,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();
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/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
deleted file mode 100644
index ba03400d7..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ /dev/null
@@ -1,222 +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);
-
- // 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..3da185dd2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -31,7 +31,6 @@ 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;
@@ -52,41 +51,37 @@ 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 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.ui.widget.ListAwareSwipeRefreshLayout;
+import org.sufficientlysecure.keychain.util.ExportHelper;
+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;
@@ -98,6 +93,8 @@ public class KeyListFragment extends LoaderFragment
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
LoaderManager.LoaderCallbacks<Cursor> {
+ ExportHelper mExportHelper;
+
private KeyListAdapter mAdapter;
private StickyListHeadersListView mStickyList;
private ListAwareSwipeRefreshLayout mSwipeRefreshLayout;
@@ -110,7 +107,16 @@ public class KeyListFragment extends LoaderFragment
private String mQuery;
private SearchView mSearchView;
- boolean hideMenu = false;
+ private FloatingActionButton mFabQrCode;
+ private FloatingActionButton mFabCloud;
+ private FloatingActionButton mFabFile;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mExportHelper = new ExportHelper(getActivity());
+ }
/**
* Load custom layout with StickyListView from library
@@ -123,6 +129,29 @@ public class KeyListFragment extends LoaderFragment
mStickyList = (StickyListHeadersListView) view.findViewById(R.id.key_list_list);
mStickyList.setOnItemClickListener(this);
+ mFabQrCode = (FloatingActionButton) view.findViewById(R.id.fab_add_qr_code);
+ mFabCloud = (FloatingActionButton) view.findViewById(R.id.fab_add_cloud);
+ mFabFile = (FloatingActionButton) view.findViewById(R.id.fab_add_file);
+
+ mFabQrCode.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ scanQrCode();
+ }
+ });
+ mFabCloud.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ searchCloud();
+ }
+ });
+ mFabFile.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ importFile();
+ }
+ });
+
mSwipeRefreshLayout = (ListAwareSwipeRefreshLayout) view.findViewById(R.id.key_list_swipe_container);
mSwipeRefreshLayout.setOnRefreshListener(new NoScrollableSwipeRefreshLayout.OnRefreshListener() {
@Override
@@ -172,8 +201,8 @@ public class KeyListFragment extends LoaderFragment
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();
+// hideMenu = true;
+// activity.invalidateOptionsMenu();
}
} else {
bar.setTitle(getActivity().getTitle());
@@ -184,8 +213,8 @@ public class KeyListFragment extends LoaderFragment
bar.setDisplayShowCustomEnabled(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- hideMenu = false;
- activity.invalidateOptionsMenu();
+// hideMenu = false;
+// activity.invalidateOptionsMenu();
}
}
}
@@ -198,90 +227,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((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;
}
- 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, checked);
+ } else {
+ mAdapter.removeSelection(position);
}
+ int count = mStickyList.getCheckedItemCount();
+ String keysSelected = getResources().getQuantityString(
+ R.plurals.key_list_selected_keys, count, count);
+ mode.setTitle(keysSelected);
+ }
- });
- }
+ });
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
@@ -368,9 +398,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 +428,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);
@@ -417,7 +444,6 @@ public class KeyListFragment extends LoaderFragment
* @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,6 +481,15 @@ 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);
@@ -463,17 +498,10 @@ public class KeyListFragment extends LoaderFragment
// 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);
-
// 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 +513,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 +594,89 @@ 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(), QrCodeScanActivity.class);
+ scanQrCode.setAction(QrCodeScanActivity.ACTION_SCAN_WITH_RESULT);
+ 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);
+ }
+ }
+
/**
* Implements StickyListHeadersAdapter from library
*/
@@ -533,7 +684,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 +775,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..2f29f9360
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
@@ -0,0 +1,50 @@
+/*
+ * 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.util.Preferences;
+
+public class MainActivity extends NavDrawerActivity {
+
+ @Override
+ public void init(Bundle savedInstanceState) {
+ super.init(savedInstanceState);
+
+ // 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();
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java
new file mode 100644
index 000000000..fb23c7d3a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java
@@ -0,0 +1,50 @@
+/*
+ * 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.os.Bundle;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.remote.ui.AppsListFragment;
+
+import it.neokree.materialnavigationdrawer.MaterialNavigationDrawer;
+
+public abstract class NavDrawerActivity extends MaterialNavigationDrawer {
+
+ @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)));
+ }
+}
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 deff648ba..d5ca08936 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
@@ -68,6 +68,8 @@ public class PassphraseDialogActivity extends FragmentActivity {
// special extra for OpenPgpService
public static final String EXTRA_DATA = "data";
+ private static final int REQUEST_CODE_ENTER_PATTERN = 2;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -90,6 +92,40 @@ public class PassphraseDialogActivity extends FragmentActivity {
show(this, keyId, serviceIntent);
}
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_ENTER_PATTERN: {
+ /*
+ * NOTE that there are 4 possible result codes!!!
+ */
+ switch (resultCode) {
+ case RESULT_OK:
+ // The user passed
+ break;
+ 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;
+ }
+
+ /*
+ * 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);
+
+ break;
+ }
+ }
+ }
+
/**
* Shows passphrase dialog to cache a new passphrase the user enters for using it later for
* encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
@@ -138,7 +174,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(theme);
- alert.setTitle(R.string.title_authentication);
+ alert.setTitle(R.string.title_unlock);
String userId;
CanonicalizedSecretKey.SecretKeyType keyType = CanonicalizedSecretKey.SecretKeyType.PASSPHRASE;
@@ -170,8 +206,11 @@ public class PassphraseDialogActivity extends FragmentActivity {
case PASSPHRASE:
message = getString(R.string.passphrase_for, userId);
break;
+ case PIN:
+ message = getString(R.string.pin_for, userId);
+ break;
case DIVERT_TO_CARD:
- message = getString(R.string.yubikey_pin, userId);
+ message = getString(R.string.yubikey_pin_for, userId);
break;
default:
message = "This should not happen!";
@@ -209,40 +248,52 @@ public class PassphraseDialogActivity extends FragmentActivity {
}
});
- // Hack to open keyboard.
- // This is the only method that I found to work across all Android versions
- // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
- // Notes: * onCreateView can't be used because we want to add buttons to the dialog
- // * opening in onActivityCreated does not work on Android 4.4
- mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mPassphraseEditText.post(new Runnable() {
- @Override
- public void run() {
- if (getActivity() == null || mPassphraseEditText == null) {
- return;
+
+ 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);
+ mInput.setVisibility(View.GONE);
+ mProgress.setVisibility(View.VISIBLE);
+ } else {
+ // Hack to open keyboard.
+ // This is the only method that I found to work across all Android versions
+ // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
+ // Notes: * onCreateView can't be used because we want to add buttons to the dialog
+ // * opening in onActivityCreated does not work on Android 4.4
+ mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPassphraseEditText.post(new Runnable() {
+ @Override
+ public void run() {
+ if (getActivity() == null || mPassphraseEditText == null) {
+ return;
+ }
+ InputMethodManager imm = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
}
- InputMethodManager imm = (InputMethodManager) getActivity()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
- }
- });
+ });
+ }
+ });
+ mPassphraseEditText.requestFocus();
+
+ mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
+ mPassphraseEditText.setOnEditorActionListener(this);
+
+ if (keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubikeyPin()) {
+ mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ } else if (keyType == CanonicalizedSecretKey.SecretKeyType.PIN) {
+ mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ } else {
+ mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
- });
- mPassphraseEditText.requestFocus();
-
- mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
- mPassphraseEditText.setOnEditorActionListener(this);
- if (keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubikeyPin()) {
- mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- } else {
- mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
}
- mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
-
AlertDialog dialog = alert.create();
dialog.setButton(DialogInterface.BUTTON_POSITIVE,
activity.getString(android.R.string.ok), (DialogInterface.OnClickListener) null);
@@ -264,7 +315,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
// Early breakout if we are dealing with a symmetric key
if (mSecretRing == null) {
PassphraseCacheService.addCachedPassphrase(getActivity(),
- Constants.key.symmetric, Constants.key.symmetric, passphrase,
+ Constants.key.symmetric, Constants.key.symmetric, passphrase,
getString(R.string.passp_cache_notif_pwd));
finishCaching(passphrase);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java
new file mode 100644
index 000000000..2e838535d
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java
@@ -0,0 +1,575 @@
+/*
+ * 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.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.FormatException;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.nfc.tech.Ndef;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.sufficientlysecure.keychain.R;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
+
+@TargetApi(Build.VERSION_CODES.HONEYCOMB)
+public class PassphraseWizardActivity extends FragmentActivity {
+//public class PassphraseWizardActivity extends FragmentActivity implements LockPatternView.OnPatternListener {
+ //create or authenticate
+ public String selectedAction;
+ //for lockpattern
+ public static char[] pattern;
+ private static String passphrase = "";
+ //nfc string
+ private static byte[] output = new byte[8];
+
+ public static final String CREATE_METHOD = "create";
+ public static final String AUTHENTICATION = "authenticate";
+
+ NfcAdapter adapter;
+ PendingIntent pendingIntent;
+ IntentFilter writeTagFilters[];
+ boolean writeMode;
+ Tag myTag;
+ boolean writeNFC = false;
+ boolean readNFC = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getActionBar() != null) {
+ getActionBar().setTitle(R.string.unlock_method);
+ }
+
+ selectedAction = getIntent().getAction();
+ if (savedInstanceState == null) {
+ SelectMethods selectMethods = new SelectMethods();
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.add(R.id.fragmentContainer, selectMethods).commit();
+ }
+ setContentView(R.layout.passphrase_wizard);
+
+ adapter = NfcAdapter.getDefaultAdapter(this);
+ if (adapter != null) {
+ pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, PassphraseWizardActivity.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
+ IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
+ tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
+ writeTagFilters = new IntentFilter[]{tagDetected};
+ }
+ }
+
+ public void noPassphrase(View view) {
+ passphrase = "";
+ Toast.makeText(this, R.string.no_passphrase_set, Toast.LENGTH_SHORT).show();
+ this.finish();
+ }
+
+ public void passphrase(View view) {
+ Passphrase passphrase = new Passphrase();
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.replace(R.id.fragmentContainer, passphrase).addToBackStack(null).commit();
+ }
+
+ public void startLockpattern(View view) {
+ if (getActionBar() != null) {
+ getActionBar().setTitle(R.string.draw_lockpattern);
+ }
+// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction);
+// LockPatternFragment lpf = LockPatternFragment.newInstance("asd");
+
+// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
+ }
+
+ public void cancel(View view) {
+ this.finish();
+ }
+
+ public void savePassphrase(View view) {
+ EditText passphrase = (EditText) findViewById(R.id.passphrase);
+ passphrase.setError(null);
+ String pw = passphrase.getText().toString();
+ //check and save passphrase
+ if (selectedAction.equals(CREATE_METHOD)) {
+ EditText passphraseAgain = (EditText) findViewById(R.id.passphraseAgain);
+ passphraseAgain.setError(null);
+ String pwAgain = passphraseAgain.getText().toString();
+
+ if (!TextUtils.isEmpty(pw)) {
+ if (!TextUtils.isEmpty(pwAgain)) {
+ if (pw.equals(pwAgain)) {
+ PassphraseWizardActivity.passphrase = pw;
+ Toast.makeText(this, getString(R.string.passphrase_saved), Toast.LENGTH_SHORT).show();
+ this.finish();
+ } else {
+ passphrase.setError(getString(R.string.passphrase_invalid));
+ passphrase.requestFocus();
+ }
+ } else {
+ passphraseAgain.setError(getString(R.string.missing_passphrase));
+ passphraseAgain.requestFocus();
+ }
+ } else {
+ passphrase.setError(getString(R.string.missing_passphrase));
+ passphrase.requestFocus();
+ }
+ }
+ //check for right passphrase
+ if (selectedAction.equals(AUTHENTICATION)) {
+ if (pw.equals(PassphraseWizardActivity.passphrase)) {
+ Toast.makeText(this, getString(R.string.unlocked), Toast.LENGTH_SHORT).show();
+ this.finish();
+ } else {
+ passphrase.setError(getString(R.string.passphrase_invalid));
+ passphrase.requestFocus();
+ }
+ }
+ }
+
+ public void NFC(View view) {
+ if (adapter != null) {
+ if (getActionBar() != null) {
+ getActionBar().setTitle(R.string.nfc_title);
+ }
+ NFCFragment nfc = new NFCFragment();
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.replace(R.id.fragmentContainer, nfc).addToBackStack(null).commit();
+
+ //if you want to create a new method or just authenticate
+ if (CREATE_METHOD.equals(selectedAction)) {
+ writeNFC = true;
+ } else if (AUTHENTICATION.equals(selectedAction)) {
+ readNFC = true;
+ }
+
+ if (!adapter.isEnabled()) {
+ showAlertDialog(getString(R.string.enable_nfc), true);
+ }
+ } else {
+ showAlertDialog(getString(R.string.no_nfc_support), false);
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
+ myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+
+ if (writeNFC && CREATE_METHOD.equals(selectedAction)) {
+ //write new password on NFC tag
+ try {
+ if (myTag != null) {
+ write(myTag);
+ 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();
+ }
+ } catch (IOException | FormatException e) {
+ e.printStackTrace();
+ }
+
+ } else if (readNFC && AUTHENTICATION.equals(selectedAction)) {
+ //read pw from NFC tag
+ try {
+ if (myTag != null) {
+ //if tag detected, read tag
+ String pwtag = read(myTag);
+ if (output != null && pwtag.equals(output.toString())) {
+
+ //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();
+ readNFC = false; //just once
+ } else {
+ //passwort doesnt match
+ TextView nfc = (TextView) findViewById(R.id.nfcText);
+ nfc.setText(R.string.nfc_wrong_tag);
+ }
+ }
+ } catch (IOException | FormatException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void write(Tag tag) throws IOException, FormatException {
+ //generate new random key and write them on the tag
+ SecureRandom sr = new SecureRandom();
+ sr.nextBytes(output);
+ NdefRecord[] records = {createRecord(output.toString())};
+ NdefMessage message = new NdefMessage(records);
+ Ndef ndef = Ndef.get(tag);
+ ndef.connect();
+ ndef.writeNdefMessage(message);
+ ndef.close();
+ }
+
+ private String read(Tag tag) throws IOException, FormatException {
+ //read string from tag
+ String password = null;
+ Ndef ndef = Ndef.get(tag);
+ ndef.connect();
+ NdefMessage ndefMessage = ndef.getCachedNdefMessage();
+
+ NdefRecord[] records = ndefMessage.getRecords();
+ for (NdefRecord ndefRecord : records) {
+ if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
+ try {
+ password = readText(ndefRecord);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ ndef.close();
+ return password;
+ }
+
+ private String readText(NdefRecord record) throws UnsupportedEncodingException {
+ //low-level method for reading nfc
+ byte[] payload = record.getPayload();
+ String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
+ int languageCodeLength = payload[0] & 0063;
+ return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
+ }
+
+ private NdefRecord createRecord(String text) throws UnsupportedEncodingException {
+ //low-level method for writing nfc
+ String lang = "en";
+ byte[] textBytes = text.getBytes();
+ byte[] langBytes = lang.getBytes("US-ASCII");
+ int langLength = langBytes.length;
+ int textLength = textBytes.length;
+ byte[] payload = new byte[1 + langLength + textLength];
+
+ // set status byte (see NDEF spec for actual bits)
+ payload[0] = (byte) langLength;
+ // copy langbytes and textbytes into payload
+ System.arraycopy(langBytes, 0, payload, 1, langLength);
+ System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength);
+ return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload);
+ }
+
+ public void showAlertDialog(String message, boolean nfc) {
+ //This method shows an AlertDialog
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+ alert.setTitle("Information").setMessage(message).setPositiveButton("Ok",
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ }
+ }
+ );
+ if (nfc) {
+
+ alert.setNeutralButton(R.string.nfc_settings,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialogInterface, int i) {
+ startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
+ }
+ }
+ );
+ }
+ alert.show();
+ }
+
+ @Override
+ public void onPause() {
+ //pause this app and free nfc intent
+ super.onPause();
+ if (adapter != null) {
+ WriteModeOff();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ //resume this app and get nfc intent
+ super.onResume();
+ if (adapter != null) {
+ WriteModeOn();
+ }
+ }
+
+ private void WriteModeOn() {
+ //enable nfc for this view
+ writeMode = true;
+ adapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null);
+ }
+
+ private void WriteModeOff() {
+ //disable nfc for this view
+ writeMode = false;
+ adapter.disableForegroundDispatch(this);
+ }
+
+ public static class SelectMethods extends Fragment {
+// private OnFragmentInteractionListener mListener;
+
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ */
+ public SelectMethods() {
+
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (getActivity().getActionBar() != null) {
+ getActivity().getActionBar().setTitle(R.string.unlock_method);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.passphrase_wizard_fragment_select_methods, container, false);
+ }
+
+// @Override
+// public void onAttach(Activity activity) {
+// super.onAttach(activity);
+// try {
+// mListener = (OnFragmentInteractionListener) activity;
+// } catch (ClassCastException e) {
+// throw new ClassCastException(activity.toString()
+// + " must implement OnFragmentInteractionListener");
+// }
+// }
+//
+// @Override
+// public void onDetach() {
+// super.onDetach();
+// mListener = null;
+// }
+
+ /**
+ * This interface must be implemented by activities that contain this
+ * fragment to allow an interaction in this fragment to be communicated
+ * to the activity and potentially other fragments contained in that
+ * activity.
+ * <p/>
+ * See the Android Training lesson <a href=
+ * "http://developer.android.com/training/basics/fragments/communicating.html"
+ * >Communicating with Other Fragments</a> for more information.
+ */
+// public static interface OnFragmentInteractionListener {
+// public void onFragmentInteraction(Uri uri);
+// }
+
+ }
+
+
+ // /**
+// * A simple {@link android.support.v4.app.Fragment} subclass.
+// * Activities that contain this fragment must implement the
+// * {@link com.haibison.android.lockpattern.Passphrase.OnFragmentInteractionListener} interface
+// * to handle interaction events.
+// */
+ public static class Passphrase extends Fragment {
+
+// private OnFragmentInteractionListener mListener;
+
+ public Passphrase() {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ View view = inflater.inflate(R.layout.passphrase_wizard_fragment_passphrase, container, false);
+ EditText passphraseAgain = (EditText) view.findViewById(R.id.passphraseAgain);
+ TextView passphraseText = (TextView) view.findViewById(R.id.passphraseText);
+ TextView passphraseTextAgain = (TextView) view.findViewById(R.id.passphraseTextAgain);
+ String selectedAction = getActivity().getIntent().getAction();
+ if (selectedAction.equals(AUTHENTICATION)) {
+ passphraseAgain.setVisibility(View.GONE);
+ passphraseTextAgain.setVisibility(View.GONE);
+ passphraseText.setText(R.string.enter_passphrase);
+// getActivity().getActionBar().setTitle(R.string.enter_passphrase);
+ } else if (selectedAction.equals(CREATE_METHOD)) {
+ passphraseAgain.setVisibility(View.VISIBLE);
+ passphraseTextAgain.setVisibility(View.VISIBLE);
+ passphraseText.setText(R.string.passphrase);
+// getActivity().getActionBar().setTitle(R.string.set_passphrase);
+ }
+ return view;
+ }
+
+// @Override
+// public void onAttach(Activity activity) {
+// super.onAttach(activity);
+// try {
+// mListener = (OnFragmentInteractionListener) activity;
+// } catch (ClassCastException e) {
+// throw new ClassCastException(activity.toString()
+// + " must implement OnFragmentInteractionListener");
+// }
+// }
+//
+// @Override
+// public void onDetach() {
+// super.onDetach();
+// mListener = null;
+// }
+
+// /**
+// * This interface must be implemented by activities that contain this
+// * fragment to allow an interaction in this fragment to be communicated
+// * to the activity and potentially other fragments contained in that
+// * activity.
+// * <p/>
+// * See the Android Training lesson <a href=
+// * "http://developer.android.com/training/basics/fragments/communicating.html"
+// * >Communicating with Other Fragments</a> for more information.
+// */
+// public interface OnFragmentInteractionListener {
+// public void onFragmentInteraction(Uri uri);
+// }
+ }
+
+
+ /**
+ * A simple {@link android.support.v4.app.Fragment} subclass.
+ * Activities that contain this fragment must implement the
+ * interface
+ * to handle interaction events.
+ * Use the method to
+ * create an instance of this fragment.
+ */
+ public static class NFCFragment extends Fragment {
+ // TODO: Rename parameter arguments, choose names that match
+ // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
+ private static final String ARG_PARAM1 = "param1";
+ private static final String ARG_PARAM2 = "param2";
+
+ // TODO: Rename and change types of parameters
+ private String mParam1;
+ private String mParam2;
+
+// private OnFragmentInteractionListener mListener;
+
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ *
+ * @param param1 Parameter 1.
+ * @param param2 Parameter 2.
+ * @return A new instance of fragment SelectMethods.
+ */
+ // TODO: Rename and change types and number of parameters
+ public static NFCFragment newInstance(String param1, String param2) {
+ NFCFragment fragment = new NFCFragment();
+ Bundle args = new Bundle();
+ args.putString(ARG_PARAM1, param1);
+ args.putString(ARG_PARAM2, param2);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ public NFCFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments() != null) {
+ mParam1 = getArguments().getString(ARG_PARAM1);
+ mParam2 = getArguments().getString(ARG_PARAM2);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.passphrase_wizard_fragment_nfc, container, false);
+ }
+
+// // TODO: Rename method, update argument and hook method into UI event
+// public void onButtonPressed(Uri uri) {
+// if (mListener != null) {
+// mListener.onFragmentInteraction(uri);
+// }
+// }
+
+// @Override
+// public void onAttach(Activity activity) {
+// super.onAttach(activity);
+// try {
+// mListener = (OnFragmentInteractionListener) activity;
+// } catch (ClassCastException e) {
+// throw new ClassCastException(activity.toString()
+// + " must implement OnFragmentInteractionListener");
+// }
+// }
+
+
+// @Override
+// public void onDetach() {
+// super.onDetach();
+// mListener = null;
+// }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java
index 5966870df..1a7a028c6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeScanActivity.java
@@ -78,7 +78,11 @@ public class QrCodeScanActivity extends FragmentActivity {
// 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
@@ -168,7 +172,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);
@@ -199,7 +203,7 @@ public class QrCodeScanActivity extends FragmentActivity {
data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
- ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<ParcelableKeyRing>();
+ ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
selectedEntries.add(keyEntry);
data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries);
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 a7ba4accf..e1a8981c4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -19,9 +19,10 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
+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;
@@ -33,41 +34,45 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
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.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.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.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 java.util.Date;
import java.util.HashMap;
-public class ViewKeyActivity extends ActionBarActivity implements
+public class ViewKeyActivity extends BaseActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
ExportHelper mExportHelper;
@@ -75,20 +80,18 @@ public class ViewKeyActivity extends ActionBarActivity implements
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;
- public static final int TAB_TRUST = 2;
-
- // 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;
@@ -99,6 +102,10 @@ 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;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -106,39 +113,20 @@ 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);
-
- setContentView(R.layout.view_key_activity);
+ setTitle(null);
- 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);
-
- 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;
- }
-
- @Override
- public int getDividerColor(int position) {
- return 0;
- }
- });
+ mBigToolbar = (RelativeLayout) findViewById(R.id.toolbar_big);
- int switchToTab = TAB_MAIN;
- Intent intent = getIntent();
- if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) {
- switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
- }
+ 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);
mDataUri = getIntent().getData();
if (mDataUri == null) {
@@ -158,39 +146,77 @@ public class ViewKeyActivity extends ActionBarActivity implements
Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+ 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.
getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
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);
-
- Bundle mainBundle = new Bundle();
- mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyMainFragment.class,
- mainBundle, getString(R.string.key_view_tab_main));
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.view_key_activity);
+ }
- Bundle shareBundle = new Bundle();
- shareBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyShareFragment.class,
- shareBundle, getString(R.string.key_view_tab_share));
+ 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 trustBundle = new Bundle();
- trustBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyTrustFragment.class, trustBundle,
- getString(R.string.key_view_tab_trust));
+ // 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
@@ -206,7 +232,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;
@@ -220,9 +246,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) {
@@ -232,6 +275,75 @@ 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, QrCodeScanActivity.class);
+ scanQrCode.setAction(QrCodeScanActivity.ACTION_SCAN_WITH_RESULT);
+ startActivityForResult(scanQrCode, 0);
+ }
+
+ private void certifyFingeprint(Uri dataUri) {
+ Intent intent = new Intent(this, CertifyFingerprintActivity.class);
+ intent.setData(dataUri);
+ 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);
@@ -273,6 +385,101 @@ 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 {
+ 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(this, ImportKeysActivity.class);
+ queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT);
+ queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint);
+
+ startActivityForResult(queryIntent, 0);
+ }
+
+ 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
+ *
+ * @param fingerprint
+ */
+ 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
*/
@@ -363,25 +570,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:
@@ -404,36 +620,136 @@ 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));
+ String fingerprint = 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;
- 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());
+ // re-create options menu based on mIsSecret, mIsVerified
+ supportInvalidateOptionsMenu();
+
+ 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(fingerprint);
+ loadQrCode(fingerprint);
+ 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(fingerprint);
+
+ 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);
+ }
}
+ mToolbar.setBackgroundColor(color);
+ mStatusBar.setBackgroundColor(color);
+ mBigToolbar.setBackgroundColor(color);
+
+ 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..894529cc5
--- /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 mainBundle = new Bundle();
+ mainBundle.putParcelable(ViewKeyAdvMainFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyAdvMainFragment.class,
+ mainBundle, getString(R.string.key_view_tab_main));
+
+ Bundle shareBundle = new Bundle();
+ shareBundle.putParcelable(ViewKeyAdvMainFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyAdvShareFragment.class,
+ shareBundle, getString(R.string.key_view_tab_share));
+
+ 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 9c37f2d94..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,
@@ -80,14 +71,14 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements
static final String CERTS_SORT_ORDER =
KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.RANK + " ASC, "
+ KeychainContract.Certs.VERIFIED + " DESC, "
- + KeychainContract.Certs.TYPE + " DESC, "
+ + KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.TYPE + " DESC, "
+ KeychainContract.Certs.SIGNER_UID + " ASC";
/**
* 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/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvMainFragment.java
index f1453c40c..c9d20f9f4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvMainFragment.java
@@ -37,20 +37,20 @@ 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.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
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.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
-public class ViewKeyMainFragment extends LoaderFragment implements
+public class ViewKeyAdvMainFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri";
@@ -80,7 +80,7 @@ public class ViewKeyMainFragment 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_main_fragment, getContainer());
+ View view = inflater.inflate(R.layout.view_key_adv_main_fragment, getContainer());
mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
mActionEdit = view.findViewById(R.id.view_key_action_edit);
@@ -199,7 +199,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements
return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
}
case LOADER_ID_USER_IDS: {
- Uri baseUri = UserIds.buildUserIdsUri(mDataUri);
+ Uri baseUri = UserPackets.buildUserIdsUri(mDataUri);
return new CursorLoader(getActivity(), baseUri,
UserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
}
@@ -265,9 +265,10 @@ public class ViewKeyMainFragment extends LoaderFragment implements
}
}
- case LOADER_ID_USER_IDS:
+ case LOADER_ID_USER_IDS: {
mUserIdsAdapter.swapCursor(data);
break;
+ }
}
setContentShown(true);
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..6208cff4e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
@@ -56,7 +56,7 @@ 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";
@@ -81,9 +81,9 @@ 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);
@@ -228,10 +228,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) {
@@ -361,7 +358,7 @@ 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.
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/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/ViewKeyTrustFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
index 677646441..5483d16b8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
@@ -62,6 +62,8 @@ 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;
@@ -100,7 +102,7 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- Uri dataUri = getArguments().getParcelable(ViewKeyMainFragment.ARG_DATA_URI);
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
if (dataUri == null) {
Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
getActivity().finish();
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 f3dce5823..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()) {
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 41c7e0cbc..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,10 @@ 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;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
@@ -34,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) {
@@ -72,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!");
@@ -127,7 +115,7 @@ public class ImportKeysListLoader
BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
try {
// parse all keyrings
- Iterator<UncachedKeyRing> it = UncachedKeyRing.fromStream(bufferedInput);
+ IteratorWithIOThrow<UncachedKeyRing> it = UncachedKeyRing.fromStream(bufferedInput);
while (it.hasNext()) {
UncachedKeyRing ring = it.next();
ImportKeysListEntry item = new ImportKeysListEntry(getContext(), ring);
@@ -139,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/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
index a2e1930d3..ad7c9e430 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
@@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.database.Cursor;
-import android.graphics.PorterDuff;
import android.graphics.Typeface;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
@@ -32,10 +31,9 @@ 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.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -45,14 +43,15 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
private LayoutInflater mInflater;
private final ArrayList<Boolean> mCheckStates;
private SaveKeyringParcel mSaveKeyringParcel;
+ private boolean mShowStatusImages;
public static final String[] USER_IDS_PROJECTION = new String[]{
- UserIds._ID,
- UserIds.USER_ID,
- UserIds.RANK,
- UserIds.VERIFIED,
- UserIds.IS_PRIMARY,
- UserIds.IS_REVOKED
+ UserPackets._ID,
+ UserPackets.USER_ID,
+ UserPackets.RANK,
+ UserPackets.VERIFIED,
+ UserPackets.IS_PRIMARY,
+ UserPackets.IS_REVOKED
};
private static final int INDEX_ID = 0;
private static final int INDEX_USER_ID = 1;
@@ -62,24 +61,30 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
private static final int INDEX_IS_REVOKED = 5;
public UserIdsAdapter(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 UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes,
+ SaveKeyringParcel saveKeyringParcel) {
+ this(context, c, flags, showCheckBoxes, false, saveKeyringParcel);
}
public UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) {
- this(context, c, flags, showCheckBoxes, null);
+ this(context, c, flags, showCheckBoxes, false, null);
}
public UserIdsAdapter(Context context, Cursor c, int flags, SaveKeyringParcel saveKeyringParcel) {
- this(context, c, flags, false, saveKeyringParcel);
+ this(context, c, flags, false, false, saveKeyringParcel);
}
public UserIdsAdapter(Context context, Cursor c, int flags) {
- this(context, c, flags, false, null);
+ this(context, c, flags, false, false, null);
}
@Override
@@ -159,12 +164,17 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
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);
@@ -186,13 +196,13 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
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;
}
}
@@ -223,7 +233,7 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
}
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);
@@ -265,7 +275,7 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
@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/AdvancedAppSettingsDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java
new file mode 100644
index 000000000..d2fa37cf7
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java
@@ -0,0 +1,72 @@
+/*
+ * 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.dialog;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+
+import org.sufficientlysecure.keychain.R;
+
+public class AdvancedAppSettingsDialogFragment extends DialogFragment {
+ private static final String ARG_PACKAGE_NAME = "package_name";
+ private static final String ARG_SIGNATURE = "signature";
+
+ /**
+ * Creates new instance of this fragment
+ */
+ 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);
+
+ frag.setArguments(args);
+ return frag;
+ }
+
+ /**
+ * Creates dialog
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final FragmentActivity activity = getActivity();
+
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
+
+ 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/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/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..6f8c477c0 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
@@ -19,18 +19,25 @@ package org.sufficientlysecure.keychain.ui.util;
import android.app.Activity;
import android.content.res.Resources;
+import android.graphics.Color;
-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.listeners.ActionClickListener;
+
+import org.sufficientlysecure.keychain.R;
/**
- * @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
@@ -39,21 +46,94 @@ public class Notify {
*/
public static void showNotify(Activity activity, CharSequence text, Style style) {
- SuperCardToast st = new SuperCardToast(activity);
- st.setText(text);
- st.setDuration(SuperToast.Duration.MEDIUM);
- switch (style){
+ Snackbar bar = Snackbar.with(activity)
+ .text(text)
+ .duration(SnackbarDuration.LENGTH_LONG);
+
+ 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 = Snackbar.with(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;
+ }
+
+ 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,
+ final ActionListener listener, int resIdAction) {
+ final Snackbar bar = Snackbar.with(activity)
+ .text(msg)
+ .actionLabel(resIdAction)
+ .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);
+ }
+ };
}
@@ -67,4 +147,15 @@ public class Notify {
public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException {
showNotify(activity, activity.getResources().getText(resId), style);
}
+
+ 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
index b3c3eb417..3403208d7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ListAwareSwipeRefreshLayout.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ListAwareSwipeRefreshLayout.java
@@ -20,13 +20,8 @@ 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 {
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 1c4eece6b..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,8 +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.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.io.InputStream;
import java.util.ArrayList;
@@ -57,10 +58,10 @@ public class ContactHelper {
KeychainContract.KeyRings.EXPIRY,
KeychainContract.KeyRings.IS_REVOKED};
public static final String[] USER_IDS_PROJECTION = new String[]{
- KeychainContract.UserIds.USER_ID
+ UserPackets.USER_ID
};
- public static final String NON_REVOKED_SELECTION = KeychainContract.UserIds.IS_REVOKED + "=0";
+ public static final String NON_REVOKED_SELECTION = UserPackets.IS_REVOKED + "=0";
public static final String[] ID_PROJECTION = new String[]{ContactsContract.RawContacts._ID};
public static final String[] SOURCE_ID_PROJECTION = new String[]{ContactsContract.RawContacts.SOURCE_ID};
@@ -72,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);
}
/**
@@ -96,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);
@@ -115,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(
@@ -127,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) {
@@ -139,7 +140,7 @@ public class ContactHelper {
}
return names;
} else {
- return new HashSet<String>();
+ return new HashSet<>();
}
}
@@ -171,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) {
@@ -181,7 +182,7 @@ public class ContactHelper {
profileCursor.close();
return emails;
} else {
- return new HashSet<String>();
+ return new HashSet<>();
}
}
@@ -200,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);
@@ -209,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<>();
}
}
@@ -220,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) {
@@ -230,7 +231,7 @@ public class ContactHelper {
}
}
mailCursor.close();
- return new ArrayList<String>(mails);
+ return new ArrayList<>(mails);
}
public static List<String> getContactNames(Context context) {
@@ -238,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) {
@@ -248,7 +249,7 @@ public class ContactHelper {
}
}
cursor.close();
- return new ArrayList<String>(names);
+ return new ArrayList<>(names);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@@ -308,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) {
@@ -350,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) {
@@ -413,7 +414,7 @@ public class ContactHelper {
int rawContactId, long masterKeyId) {
ops.add(selectByRawContactAndItemType(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI),
rawContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE).build());
- Cursor ids = resolver.query(KeychainContract.UserIds.buildUserIdsUri(masterKeyId),
+ Cursor ids = resolver.query(UserPackets.buildUserIdsUri(masterKeyId),
USER_IDS_PROJECTION, NON_REVOKED_SELECTION, null, null);
if (ids != null) {
while (ids.moveToNext()) {
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/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);
}
}