aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Kepkowski <michal.kepkowski@sagiton.pl>2016-04-06 19:16:10 +0200
committerMichal Kepkowski <michal.kepkowski@sagiton.pl>2016-04-06 19:16:10 +0200
commitd9988a8cdacb38ce1a1874d1fbb65ff7eb8f8acd (patch)
treea2c6bad3a5da60d4e1fcfc75f2588821d699ea01
parentdac5f1db08b4dea5893d9fe0a1ab0daad5b44f09 (diff)
parent88ba5688d8e110fc6676c701359bb2bbca7efb77 (diff)
downloadopen-keychain-d9988a8cdacb38ce1a1874d1fbb65ff7eb8f8acd.tar.gz
open-keychain-d9988a8cdacb38ce1a1874d1fbb65ff7eb8f8acd.tar.bz2
open-keychain-d9988a8cdacb38ce1a1874d1fbb65ff7eb8f8acd.zip
Merge branch 'master' into okhttp3
-rw-r--r--OpenKeychain/build.gradle8
-rw-r--r--OpenKeychain/src/main/AndroidManifest.xml10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java113
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java42
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/receiver/NetworkReceiver.java52
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java16
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java180
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiUserIdsFragment.java223
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java21
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java473
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java77
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java55
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.pngbin0 -> 923 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.pngbin0 -> 623 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_change_grey_24dp.pngbin0 -> 1066 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.pngbin0 -> 1612 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.pngbin0 -> 2223 bytes
-rw-r--r--OpenKeychain/src/main/res/layout/add_subkey_dialog.xml163
-rw-r--r--OpenKeychain/src/main/res/layout/certify_item.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/certify_key_fragment.xml5
-rw-r--r--OpenKeychain/src/main/res/layout/multi_user_ids_fragment.xml6
-rw-r--r--OpenKeychain/src/main/res/layout/two_line_spinner_dropdown_item.xml32
-rw-r--r--OpenKeychain/src/main/res/layout/upload_key_activity.xml6
-rw-r--r--OpenKeychain/src/main/res/values/colors.xml1
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml33
-rw-r--r--OpenKeychain/src/main/res/xml/sync_preferences.xml6
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java7
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java42
-rw-r--r--README.md4
52 files changed, 990 insertions, 913 deletions
diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle
index 8f7f31bed..cd8142174 100644
--- a/OpenKeychain/build.gradle
+++ b/OpenKeychain/build.gradle
@@ -49,7 +49,7 @@ dependencies {
compile 'com.getbase:floatingactionbutton:1.10.1'
compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0'
compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
- compile 'com.splitwise:tokenautocomplete:2.0.5@aar'
+ compile 'com.splitwise:tokenautocomplete:2.0.7@aar'
compile 'com.github.pinball83:masked-edittext:1.0.3'
compile 'se.emilsjolander:stickylistheaders:2.7.0'
compile 'org.sufficientlysecure:html-textview:1.3'
@@ -100,7 +100,7 @@ dependencyVerification {
'com.getbase:floatingactionbutton:3edefa511aac4d90794c7b0496aca59cff2eee1e32679247b4f85acbeee05240',
'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13',
'org.ocpsoft.prettytime:prettytime:ef7098d973ae78b57d1a22dc37d3b8a771bf030301300e24055d676b6cdc5e75',
- 'com.splitwise:tokenautocomplete:f86b8cd80e8fa1336f47d86b8ce862e1a40bdc8e206ac38c56c8e41a3d05a331',
+ 'com.splitwise:tokenautocomplete:f56239588390f103b270b7c12361d99b06313a5a0410dc7f66e241ac4baf9baa',
'com.github.pinball83:masked-edittext:b1913d86482c7066ebb7831696773ac131865dc441cf8a3fc41d3b7d5691724e',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
'org.sufficientlysecure:donations:96f8197bab26dfe41900d824f10f8f1914519cd62eedb77bdac5b223eccdf0a6',
@@ -145,8 +145,8 @@ android {
defaultConfig {
minSdkVersion 15
targetSdkVersion 23
- versionCode 39200
- versionName "3.9.2"
+ versionCode 39400
+ versionName "3.9.4"
applicationId "org.sufficientlysecure.keychain"
// the androidjunitrunner is broken regarding coverage, see here:
// https://code.google.com/p/android/issues/detail?id=170607
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index 50ab9aaae..74bf936b4 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -76,6 +76,7 @@
<!-- other group (for free) -->
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
@@ -89,6 +90,15 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Keychain.Light">
+ <!-- broadcast receiver for Wi-Fi Connection -->
+ <receiver
+ android:name=".receiver.NetworkReceiver"
+ android:enabled="false"
+ android:exported="true" >
+ <intent-filter>
+ <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
+ </intent-filter>
+ </receiver>
<!-- singleTop for NFC dispatch, see SecurityTokenOperationActivity -->
<activity
android:name=".ui.MainActivity"
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 53fb5afc6..fd6e903fa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -118,6 +118,7 @@ public final class Constants {
// keyserver sync settings
public static final String SYNC_CONTACTS = "syncContacts";
public static final String SYNC_KEYSERVER = "syncKeyserver";
+ public static final String ENABLE_WIFI_SYNC_ONLY = "enableWifiSyncOnly";
// other settings
public static final String EXPERIMENTAL_ENABLE_WORD_CONFIRM = "experimentalEnableWordConfirm";
public static final String EXPERIMENTAL_ENABLE_LINKED_IDENTITIES = "experimentalEnableLinkedIdentities";
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index e1f61a5ef..2f0ebe904 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -100,6 +100,11 @@ public class KeychainApplication extends Application {
// Add OpenKeychain account to Android to link contacts with keys and keyserver sync
createAccountIfNecessary(this);
+ if (Preferences.getKeyserverSyncEnabled(this)) {
+ // will update a keyserver sync if the interval has changed
+ KeyserverSyncAdapterService.enableKeyserverSync(this);
+ }
+
// if first time, enable keyserver and contact sync
if (Preferences.getPreferences(this).isFirstTime()) {
KeyserverSyncAdapterService.enableKeyserverSync(this);
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 ec2fddbd0..a3979904c 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
@@ -566,8 +566,6 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_ERROR_BAD_SECURITY_TOKEN_SIZE(LogLevel.ERROR, R.string.edit_key_error_bad_security_token_size),
MSG_MF_ERROR_BAD_SECURITY_TOKEN_STRIPPED(LogLevel.ERROR, R.string.edit_key_error_bad_security_token_stripped),
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_PIN (LogLevel.INFO, R.string.msg_mf_pin),
MSG_MF_ADMIN_PIN (LogLevel.INFO, R.string.msg_mf_admin_pin),
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 e43548165..102e11674 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -18,6 +18,23 @@
package org.sufficientlysecure.keychain.pgp;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Stack;
+import java.util.concurrent.atomic.AtomicBoolean;
+
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.sig.Features;
@@ -57,13 +74,12 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat
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.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcKeyToCardOperationsBuilder;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -71,22 +87,6 @@ import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Primes;
import org.sufficientlysecure.keychain.util.ProgressScaler;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.SignatureException;
-import java.security.spec.ECGenParameterSpec;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.Stack;
-import java.util.concurrent.atomic.AtomicBoolean;
-
/**
* This class is the single place where ALL operations that actually modify a PGP public or secret
* key take place.
@@ -1058,8 +1058,8 @@ public class PgpKeyOperation {
log.add(LogType.MSG_MF_PASSPHRASE, indent);
indent += 1;
- sKR = applyNewUnlock(sKR, masterPublicKey, masterPrivateKey,
- cryptoInput.getPassphrase(), saveParcel.mNewUnlock, log, indent);
+ sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(),
+ saveParcel.mNewUnlock.mNewPassphrase, log, indent);
if (sKR == null) {
// The error has been logged above, just return a bad state
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -1191,76 +1191,6 @@ public class PgpKeyOperation {
}
-
- private static PGPSecretKeyRing applyNewUnlock(
- PGPSecretKeyRing sKR,
- PGPPublicKey masterPublicKey,
- PGPPrivateKey masterPrivateKey,
- Passphrase 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(),
- PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
- .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(),
- PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
- .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.
*/
@@ -1294,8 +1224,7 @@ public class PgpKeyOperation {
PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(newPassphrase.getCharArray());
- // noinspection unchecked
- for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
+ for (PGPSecretKey sKey : new IterableIterator<>(sKR.getSecretKeys())) {
log.add(LogType.MSG_MF_PASSPHRASE_KEY, indent,
KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID()));
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 d696b9d70..b0db36b06 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -35,6 +35,8 @@ import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
+import android.support.annotation.VisibleForTesting;
+
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.SignatureSubpacketTags;
@@ -42,15 +44,22 @@ import org.bouncycastle.bcpg.UserAttributeSubpacketTags;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.openpgp.PGPKeyRing;
import org.bouncycastle.openpgp.PGPObjectFactory;
+import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureList;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@@ -1310,4 +1319,37 @@ public class UncachedKeyRing {
|| algorithm == PGPPublicKey.ECDH;
}
+ // ONLY TO BE USED FOR TESTING!!
+ @VisibleForTesting
+ public static UncachedKeyRing forTestingOnlyAddDummyLocalSignature(
+ UncachedKeyRing uncachedKeyRing, String passphrase) throws Exception {
+ PGPSecretKeyRing sKR = (PGPSecretKeyRing) uncachedKeyRing.mRing;
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ PGPPrivateKey masterPrivateKey = sKR.getSecretKey().extractPrivateKey(keyDecryptor);
+ PGPPublicKey masterPublicKey = uncachedKeyRing.mRing.getPublicKey();
+
+ // add packet with "pin" notation data
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
+ PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
+ .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, "dummynotationdata", "some data");
+ 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 new UncachedKeyRing(sKR);
+ }
+
}
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 2a4d898bc..04c14491b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -54,7 +54,7 @@ import java.io.IOException;
*/
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db";
- private static final int DATABASE_VERSION = 15;
+ private static final int DATABASE_VERSION = 16;
static Boolean apgHack = false;
private Context mContext;
@@ -279,40 +279,45 @@ public class KeychainDatabase extends SQLiteOpenHelper {
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
}
- case 9:
+ case 8:
// tbale name for user_ids changed to user_packets
db.execSQL("DROP TABLE IF EXISTS certs");
db.execSQL("DROP TABLE IF EXISTS user_ids");
db.execSQL(CREATE_USER_PACKETS);
db.execSQL(CREATE_CERTS);
- case 10:
+ case 9:
// do nothing here, just consolidate
- case 11:
+ case 10:
// fix problems in database, see #1402 for details
// https://github.com/open-keychain/open-keychain/issues/1402
db.execSQL("DELETE FROM api_accounts WHERE key_id BETWEEN 0 AND 3");
- case 12:
+ case 11:
db.execSQL(CREATE_UPDATE_KEYS);
- case 13:
+ case 12:
// do nothing here, just consolidate
- case 14:
+ case 13:
db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ");");
db.execSQL("CREATE INDEX uids_by_rank ON user_packets (" + UserPacketsColumns.RANK + ", "
+ UserPacketsColumns.USER_ID + ", " + UserPacketsColumns.MASTER_KEY_ID + ");");
db.execSQL("CREATE INDEX verified_certs ON certs ("
+ CertsColumns.VERIFIED + ", " + CertsColumns.MASTER_KEY_ID + ");");
- case 15:
+ case 14:
db.execSQL("ALTER TABLE user_packets ADD COLUMN name TEXT");
db.execSQL("ALTER TABLE user_packets ADD COLUMN email TEXT");
db.execSQL("ALTER TABLE user_packets ADD COLUMN comment TEXT");
+ case 15:
+ db.execSQL("CREATE INDEX uids_by_name ON user_packets (name COLLATE NOCASE)");
+ db.execSQL("CREATE INDEX uids_by_email ON user_packets (email COLLATE NOCASE)");
+ if (oldVersion == 14) {
+ // no consolidate necessary
+ return;
+ }
}
// 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 9fbee0a67..9c5d0c054 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -312,12 +312,14 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(KeyRings.EMAIL, Tables.USER_PACKETS + "." + UserPackets.EMAIL);
projectionMap.put(KeyRings.COMMENT, Tables.USER_PACKETS + "." + UserPackets.COMMENT);
projectionMap.put(KeyRings.HAS_DUPLICATE_USER_ID,
- "(EXISTS (SELECT * FROM " + Tables.USER_PACKETS + " AS dups"
+ "(EXISTS (SELECT * FROM " + Tables.USER_PACKETS + " AS dups"
+ " WHERE dups." + UserPackets.MASTER_KEY_ID
+ " != " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND dups." + UserPackets.RANK + " = 0"
- + " AND dups." + UserPackets.USER_ID
- + " = "+ Tables.USER_PACKETS + "." + UserPackets.USER_ID
+ + " AND dups." + UserPackets.NAME
+ + " = " + Tables.USER_PACKETS + "." + UserPackets.NAME + " COLLATE NOCASE"
+ + " AND dups." + UserPackets.EMAIL
+ + " = " + Tables.USER_PACKETS + "." + UserPackets.EMAIL + " COLLATE NOCASE"
+ ")) AS " + KeyRings.HAS_DUPLICATE_USER_ID);
projectionMap.put(KeyRings.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
projectionMap.put(KeyRings.PUBKEY_DATA,
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/receiver/NetworkReceiver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/receiver/NetworkReceiver.java
new file mode 100644
index 000000000..7c103a9a3
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/receiver/NetworkReceiver.java
@@ -0,0 +1,52 @@
+package org.sufficientlysecure.keychain.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class NetworkReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ ConnectivityManager conn = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = conn.getActiveNetworkInfo();
+ boolean isTypeWifi = (networkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+ boolean isConnected = networkInfo.isConnected();
+
+ if (isTypeWifi && isConnected) {
+
+ // broadcaster receiver disabled
+ setWifiReceiverComponent(false, context);
+ Intent serviceIntent = new Intent(context, KeyserverSyncAdapterService.class);
+ serviceIntent.setAction(KeyserverSyncAdapterService.ACTION_SYNC_NOW);
+ context.startService(serviceIntent);
+ }
+ }
+
+ public void setWifiReceiverComponent(Boolean isEnabled, Context context) {
+
+ PackageManager pm = context.getPackageManager();
+ ComponentName compName = new ComponentName(context,
+ NetworkReceiver.class);
+
+ if (isEnabled) {
+ pm.setComponentEnabledSetting(compName,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
+ Log.d(Constants.TAG, "Wifi Receiver is enabled!");
+ } else {
+ pm.setComponentEnabledSetting(compName,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
+ Log.d(Constants.TAG, "Wifi Receiver is disabled!");
+ }
+ }
+}
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 15f8a47db..965d15138 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
@@ -144,12 +144,13 @@ public class ContactSyncAdapterService extends Service {
public static void requestContactsSync() {
// if user has disabled automatic sync, do nothing
- if (!ContentResolver.getSyncAutomatically(
- new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE),
- ContactsContract.AUTHORITY)) {
+ boolean isSyncEnabled = ContentResolver.getSyncAutomatically(new Account
+ (Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), ContactsContract.AUTHORITY);
+
+ if (!isSyncEnabled) {
return;
}
-
+
Bundle extras = new Bundle();
// no need to wait, do it immediately
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
index 1c59782fc..b71fbada8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
@@ -11,11 +11,14 @@ import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.PeriodicSync;
import android.content.SyncResult;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -35,6 +38,7 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.receiver.NetworkReceiver;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -68,7 +72,7 @@ public class KeyserverSyncAdapterService extends Service {
private static final String ACTION_IGNORE_TOR = "ignore_tor";
private static final String ACTION_UPDATE_ALL = "update_all";
- private static final String ACTION_SYNC_NOW = "sync_now";
+ public static final String ACTION_SYNC_NOW = "sync_now";
private static final String ACTION_DISMISS_NOTIFICATION = "cancel_sync";
private static final String ACTION_START_ORBOT = "start_orbot";
private static final String ACTION_CANCEL = "cancel";
@@ -176,8 +180,25 @@ public class KeyserverSyncAdapterService extends Service {
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
- Log.d(Constants.TAG, "Performing a keyserver sync!");
+ Preferences prefs = Preferences.getPreferences(getContext());
+
+ // for a wifi-ONLY sync
+ if (prefs.getWifiOnlySync()) {
+
+ ConnectivityManager connMgr = (ConnectivityManager)
+ getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ boolean isNotOnWifi = !(networkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+ boolean isNotConnected = !(networkInfo.isConnected());
+
+ // if Wi-Fi connection doesn't exist then receiver is enabled
+ if (isNotOnWifi && isNotConnected) {
+ new NetworkReceiver().setWifiReceiverComponent(true, getContext());
+ return;
+ }
+ }
+ Log.d(Constants.TAG, "Performing a keyserver sync!");
PowerManager pm = (PowerManager) KeyserverSyncAdapterService.this
.getSystemService(Context.POWER_SERVICE);
@SuppressWarnings("deprecation") // our min is API 15, deprecated only in 20
@@ -509,6 +530,10 @@ public class KeyserverSyncAdapterService extends Service {
return builder.build();
}
+ /**
+ * creates a new sync if one does not exist, or updates an existing sync if the sync interval
+ * has changed.
+ */
public static void enableKeyserverSync(Context context) {
Account account = KeychainApplication.createAccountIfNecessary(context);
@@ -519,12 +544,26 @@ public class KeyserverSyncAdapterService extends Service {
ContentResolver.setIsSyncable(account, Constants.PROVIDER_AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, Constants.PROVIDER_AUTHORITY, true);
- ContentResolver.addPeriodicSync(
- account,
- Constants.PROVIDER_AUTHORITY,
- new Bundle(),
- SYNC_INTERVAL
- );
+
+ boolean intervalChanged = false;
+ boolean syncExists = Preferences.getKeyserverSyncEnabled(context);
+
+ if (syncExists) {
+ long oldInterval = ContentResolver.getPeriodicSyncs(
+ account, Constants.PROVIDER_AUTHORITY).get(0).period;
+ if (oldInterval != SYNC_INTERVAL) {
+ intervalChanged = true;
+ }
+ }
+
+ if (!syncExists || intervalChanged) {
+ ContentResolver.addPeriodicSync(
+ account,
+ Constants.PROVIDER_AUTHORITY,
+ new Bundle(),
+ SYNC_INTERVAL
+ );
+ }
}
private boolean isSyncEnabled() {
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 472eb3b18..dc892ecc8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -356,29 +356,21 @@ public class SaveKeyringParcel implements Parcelable {
// The new passphrase to use
public final Passphrase mNewPassphrase;
- // A new pin to use. Must only contain [0-9]+
- public final Passphrase mNewPin;
public ChangeUnlockParcel(Passphrase newPassphrase) {
- this(newPassphrase, null);
- }
- public ChangeUnlockParcel(Passphrase newPassphrase, Passphrase newPin) {
- if (newPassphrase == null && newPin == null) {
- throw new RuntimeException("Cannot set both passphrase and pin. THIS IS A BUG!");
+ if (newPassphrase == null) {
+ throw new AssertionError("newPassphrase must be non-null. THIS IS A BUG!");
}
mNewPassphrase = newPassphrase;
- mNewPin = newPin;
}
public ChangeUnlockParcel(Parcel source) {
mNewPassphrase = source.readParcelable(Passphrase.class.getClassLoader());
- mNewPin = source.readParcelable(Passphrase.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeParcelable(mNewPassphrase, flags);
- destination.writeParcelable(mNewPin, flags);
}
@Override
@@ -397,9 +389,7 @@ public class SaveKeyringParcel implements Parcelable {
};
public String toString() {
- return mNewPassphrase != null
- ? ("passphrase (" + mNewPassphrase + ")")
- : ("pin (" + mNewPin + ")");
+ return "passphrase (" + mNewPassphrase + ")";
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java
index f1d9b270d..e7ff6ce46 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java
@@ -43,6 +43,7 @@ import android.support.v4.app.FragmentManager.OnBackStackChangedListener;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
+import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -237,6 +238,30 @@ public class BackupCodeFragment extends CryptoOperationFragment<BackupKeyringPar
mCodeEditText.setImeOptions(EditorInfo.IME_ACTION_DONE);
setupEditTextSuccessListener(mCodeEditText);
+ // prevent selection action mode, partially circumventing text selection bug
+ mCodeEditText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+
+ }
+ });
+
+
TextView codeDisplayText = (TextView) view.findViewById(R.id.backup_code_display);
setupAutomaticLinebreak(codeDisplayText);
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 3845e07cb..09149716c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -28,7 +28,8 @@ import org.sufficientlysecure.keychain.ui.base.BaseActivity;
public class CertifyKeyActivity extends BaseActivity {
public static final String EXTRA_RESULT = "operation_result";
- public static final String EXTRA_KEY_IDS = "extra_key_ids";
+ // For sending masterKeyIds to MultiUserIdsFragment to display list of keys
+ public static final String EXTRA_KEY_IDS = MultiUserIdsFragment.EXTRA_KEY_IDS ;
public static final String EXTRA_CERTIFY_KEY_ID = "certify_key_id";
@Override
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 357b445f0..ad39ff43d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
@@ -62,58 +62,26 @@ import java.util.ArrayList;
import java.util.Date;
public class CertifyKeyFragment
- extends CachingCryptoOperationFragment<CertifyActionsParcel, CertifyResult>
- implements LoaderManager.LoaderCallbacks<Cursor> {
-
- public static final String ARG_CHECK_STATES = "check_states";
+ extends CachingCryptoOperationFragment<CertifyActionsParcel, CertifyResult> {
private CheckBox mUploadKeyCheckbox;
- ListView mUserIds;
private CertifyKeySpinner mCertifyKeySpinner;
- private long[] mPubMasterKeyIds;
-
- public static final String[] USER_IDS_PROJECTION = new String[]{
- 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;
- @SuppressWarnings("unused")
- private static final int INDEX_IS_PRIMARY = 3;
- @SuppressWarnings("unused")
- private static final int INDEX_IS_REVOKED = 4;
-
- private MultiUserIdsAdapter mUserIdsAdapter;
+ private MultiUserIdsFragment mMultiUserIdsFragment;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mPubMasterKeyIds = getActivity().getIntent().getLongArrayExtra(CertifyKeyActivity.EXTRA_KEY_IDS);
- if (mPubMasterKeyIds == null) {
- Log.e(Constants.TAG, "List of key ids to certify missing!");
- getActivity().finish();
- return;
- }
-
- ArrayList<Boolean> checkedStates;
- if (savedInstanceState != null) {
- checkedStates = (ArrayList<Boolean>) savedInstanceState.getSerializable(ARG_CHECK_STATES);
- // key spinner and the checkbox keep their own state
- } else {
- checkedStates = null;
-
+ if (savedInstanceState == null) {
// preselect certify key id if given
long certifyKeyId = getActivity().getIntent()
.getLongExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none);
if (certifyKeyId != Constants.key.none) {
try {
- CachedPublicKeyRing key = (new ProviderHelper(getActivity())).getCachedPublicKeyRing(certifyKeyId);
+ CachedPublicKeyRing key = (new ProviderHelper(getActivity()))
+ .getCachedPublicKeyRing(certifyKeyId);
if (key.canCertify()) {
mCertifyKeySpinner.setPreSelectedKeyId(certifyKeyId);
}
@@ -121,15 +89,8 @@ public class CertifyKeyFragment
Log.e(Constants.TAG, "certify certify check failed", e);
}
}
-
}
- mUserIdsAdapter = new MultiUserIdsAdapter(getActivity(), null, 0, checkedStates);
- mUserIds.setAdapter(mUserIdsAdapter);
- mUserIds.setDividerHeight(0);
-
- getLoaderManager().initLoader(0, null, this);
-
OperationResult result = getActivity().getIntent().getParcelableExtra(CertifyKeyActivity.EXTRA_RESULT);
if (result != null) {
// display result from import
@@ -138,21 +99,13 @@ public class CertifyKeyFragment
}
@Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- ArrayList<Boolean> states = mUserIdsAdapter.getCheckStates();
- // no proper parceling method available :(
- outState.putSerializable(ARG_CHECK_STATES, states);
- }
-
- @Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.certify_key_fragment, null);
mCertifyKeySpinner = (CertifyKeySpinner) view.findViewById(R.id.certify_key_spinner);
mUploadKeyCheckbox = (CheckBox) view.findViewById(R.id.sign_key_upload_checkbox);
- mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
+ mMultiUserIdsFragment = (MultiUserIdsFragment)
+ getChildFragmentManager().findFragmentById(R.id.multi_user_ids_fragment);
// make certify image gray, like action icons
ImageView vActionCertifyImage =
@@ -184,127 +137,10 @@ public class CertifyKeyFragment
}
@Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- Uri uri = UserPackets.buildUserIdsUri();
-
- String selection, ids[];
- {
- // generate placeholders and string selection args
- ids = new String[mPubMasterKeyIds.length];
- StringBuilder placeholders = new StringBuilder("?");
- for (int i = 0; i < mPubMasterKeyIds.length; i++) {
- ids[i] = Long.toString(mPubMasterKeyIds[i]);
- if (i != 0) {
- placeholders.append(",?");
- }
- }
- // put together selection string
- 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_PACKETS + "." + UserPackets.MASTER_KEY_ID + " ASC"
- + ", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC"
- );
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-
- MatrixCursor matrix = new MatrixCursor(new String[]{
- "_id", "user_data", "grouped"
- }) {
- @Override
- public byte[] getBlob(int column) {
- return super.getBlob(column);
- }
- };
- data.moveToFirst();
-
- long lastMasterKeyId = 0;
- String lastName = "";
- ArrayList<String> uids = new ArrayList<>();
-
- boolean header = true;
-
- // Iterate over all rows
- while (!data.isAfterLast()) {
- long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
- String userId = data.getString(INDEX_USER_ID);
- KeyRing.UserId pieces = KeyRing.splitUserId(userId);
-
- // Two cases:
-
- boolean grouped = masterKeyId == lastMasterKeyId;
- boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces.name);
- // Remember for next loop
- lastName = pieces.name;
-
- Log.d(Constants.TAG, Long.toString(masterKeyId, 16) + (grouped ? "grouped" : "not grouped"));
-
- if (!subGrouped) {
- // 1. This name should NOT be grouped with the previous, so we flush the buffer
-
- Parcel p = Parcel.obtain();
- p.writeStringList(uids);
- byte[] d = p.marshall();
- p.recycle();
-
- matrix.addRow(new Object[]{
- lastMasterKeyId, d, header ? 1 : 0
- });
- // indicate that we have a header for this masterKeyId
- header = false;
-
- // Now clear the buffer, and add the new user id, for the next round
- uids.clear();
-
- }
-
- // 2. This name should be grouped with the previous, just add to buffer
- uids.add(userId);
- lastMasterKeyId = masterKeyId;
-
- // If this one wasn't grouped, the next one's gotta be a header
- if (!grouped) {
- header = true;
- }
-
- // Regardless of the outcome, move to next entry
- data.moveToNext();
-
- }
-
- // If there is anything left in the buffer, flush it one last time
- if (!uids.isEmpty()) {
-
- Parcel p = Parcel.obtain();
- p.writeStringList(uids);
- byte[] d = p.marshall();
- p.recycle();
-
- matrix.addRow(new Object[]{
- lastMasterKeyId, d, header ? 1 : 0
- });
-
- }
-
- mUserIdsAdapter.swapCursor(matrix);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- mUserIdsAdapter.swapCursor(null);
- }
-
- @Override
public CertifyActionsParcel createOperationInput() {
// Bail out if there is not at least one user id selected
- ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
+ ArrayList<CertifyAction> certifyActions = mMultiUserIdsFragment.getSelectedCertifyActions();
if (certifyActions.isEmpty()) {
Notify.create(getActivity(), "No identities selected!",
Notify.Style.ERROR).show();
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 b53bfc1d0..cbf862074 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -278,17 +278,17 @@ public class CreateKeyFinalFragment extends Fragment {
2048, null, KeyFlags.AUTHENTICATION, 0L));
// use empty passphrase
- saveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase(), null);
+ saveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
} else {
saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
- 4096, null, KeyFlags.CERTIFY_OTHER, 0L));
+ 3072, null, KeyFlags.CERTIFY_OTHER, 0L));
saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
- 4096, null, KeyFlags.SIGN_DATA, 0L));
+ 3072, null, KeyFlags.SIGN_DATA, 0L));
saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
- 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
+ 3072, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
saveKeyringParcel.mNewUnlock = createKeyActivity.mPassphrase != null
- ? new ChangeUnlockParcel(createKeyActivity.mPassphrase, null)
+ ? new ChangeUnlockParcel(createKeyActivity.mPassphrase)
: null;
}
String userId = KeyRing.createUserId(
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 2d94d0d93..4cbb4724e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -339,8 +339,7 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
// cache new returned passphrase!
mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(
- (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
- null
+ (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE)
);
}
}
@@ -562,15 +561,9 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
}
private void addSubkey() {
- boolean willBeMasterKey;
- if (mSubkeysAdapter != null) {
- willBeMasterKey = mSubkeysAdapter.getCount() == 0 && mSubkeysAddedAdapter.getCount() == 0;
- } else {
- willBeMasterKey = mSubkeysAddedAdapter.getCount() == 0;
- }
-
+ // new subkey will never be a masterkey, as masterkey cannot be removed
AddSubkeyDialogFragment addSubkeyDialogFragment =
- AddSubkeyDialogFragment.newInstance(willBeMasterKey);
+ AddSubkeyDialogFragment.newInstance(false);
addSubkeyDialogFragment
.setOnAlgorithmSelectedListener(
new AddSubkeyDialogFragment.OnAlgorithmSelectedListener() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
index ca5d20fb9..b2b85ec14 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
@@ -79,9 +79,6 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment {
mSignKeySpinner = (KeySpinner) view.findViewById(R.id.sign);
mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list);
mEncryptKeyView.setThreshold(1); // Start working from first character
- // TODO: workaround for bug in TokenAutoComplete,
- // see https://github.com/open-keychain/open-keychain/issues/1636
- mEncryptKeyView.setDeletionStyle(TokenCompleteTextView.TokenDeleteStyle.ToString);
final ViewAnimator vSignatureIcon = (ViewAnimator) view.findViewById(R.id.result_signature_icon);
mSignKeySpinner.setOnKeyChangedListener(new OnKeyChangedListener() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiUserIdsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiUserIdsFragment.java
new file mode 100644
index 000000000..8ba695cf7
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiUserIdsFragment.java
@@ -0,0 +1,223 @@
+package org.sufficientlysecure.keychain.ui;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
+import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
+
+public class MultiUserIdsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{
+ public static final String ARG_CHECK_STATES = "check_states";
+ public static final String EXTRA_KEY_IDS = "extra_key_ids";
+ private boolean checkboxVisibility = true;
+
+ ListView mUserIds;
+ private MultiUserIdsAdapter mUserIdsAdapter;
+
+ private long[] mPubMasterKeyIds;
+
+ public static final String[] USER_IDS_PROJECTION = new String[]{
+ KeychainContract.UserPackets._ID,
+ KeychainContract.UserPackets.MASTER_KEY_ID,
+ KeychainContract.UserPackets.USER_ID,
+ KeychainContract.UserPackets.IS_PRIMARY,
+ KeychainContract.UserPackets.IS_REVOKED
+ };
+ private static final int INDEX_MASTER_KEY_ID = 1;
+ private static final int INDEX_USER_ID = 2;
+ @SuppressWarnings("unused")
+ private static final int INDEX_IS_PRIMARY = 3;
+ @SuppressWarnings("unused")
+ private static final int INDEX_IS_REVOKED = 4;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.multi_user_ids_fragment, null);
+
+ mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mPubMasterKeyIds = getActivity().getIntent().getLongArrayExtra(EXTRA_KEY_IDS);
+ if (mPubMasterKeyIds == null) {
+ Log.e(Constants.TAG, "List of key ids to certify missing!");
+ getActivity().finish();
+ return;
+ }
+
+ ArrayList<Boolean> checkedStates = null;
+ if (savedInstanceState != null) {
+ checkedStates = (ArrayList<Boolean>) savedInstanceState.getSerializable(ARG_CHECK_STATES);
+ }
+
+ mUserIdsAdapter = new MultiUserIdsAdapter(getActivity(), null, 0, checkedStates, checkboxVisibility);
+ mUserIds.setAdapter(mUserIdsAdapter);
+ mUserIds.setDividerHeight(0);
+
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ ArrayList<Boolean> states = mUserIdsAdapter.getCheckStates();
+ // no proper parceling method available :(
+ outState.putSerializable(ARG_CHECK_STATES, states);
+ }
+
+ public ArrayList<CertifyActionsParcel.CertifyAction> getSelectedCertifyActions() {
+ if (!checkboxVisibility) {
+ throw new AssertionError("Item selection not allowed");
+ }
+
+ return mUserIdsAdapter.getSelectedCertifyActions();
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ Uri uri = KeychainContract.UserPackets.buildUserIdsUri();
+
+ String selection, ids[];
+ {
+ // generate placeholders and string selection args
+ ids = new String[mPubMasterKeyIds.length];
+ StringBuilder placeholders = new StringBuilder("?");
+ for (int i = 0; i < mPubMasterKeyIds.length; i++) {
+ ids[i] = Long.toString(mPubMasterKeyIds[i]);
+ if (i != 0) {
+ placeholders.append(",?");
+ }
+ }
+ // put together selection string
+ selection = KeychainContract.UserPackets.IS_REVOKED + " = 0" + " AND "
+ + KeychainDatabase.Tables.USER_PACKETS + "." + KeychainContract.UserPackets.MASTER_KEY_ID
+ + " IN (" + placeholders + ")";
+ }
+
+ return new CursorLoader(getActivity(), uri,
+ USER_IDS_PROJECTION, selection, ids,
+ KeychainDatabase.Tables.USER_PACKETS + "." + KeychainContract.UserPackets.MASTER_KEY_ID + " ASC"
+ + ", " + KeychainDatabase.Tables.USER_PACKETS + "." + KeychainContract.UserPackets.USER_ID + " ASC"
+ );
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+
+ MatrixCursor matrix = new MatrixCursor(new String[]{
+ "_id", "user_data", "grouped"
+ }) {
+ @Override
+ public byte[] getBlob(int column) {
+ return super.getBlob(column);
+ }
+ };
+ data.moveToFirst();
+
+ long lastMasterKeyId = 0;
+ String lastName = "";
+ ArrayList<String> uids = new ArrayList<>();
+
+ boolean header = true;
+
+ // Iterate over all rows
+ while (!data.isAfterLast()) {
+ long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
+ String userId = data.getString(INDEX_USER_ID);
+ KeyRing.UserId pieces = KeyRing.splitUserId(userId);
+
+ // Two cases:
+
+ boolean grouped = masterKeyId == lastMasterKeyId;
+ boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces.name);
+ // Remember for next loop
+ lastName = pieces.name;
+
+ Log.d(Constants.TAG, Long.toString(masterKeyId, 16) + (grouped ? "grouped" : "not grouped"));
+
+ if (!subGrouped) {
+ // 1. This name should NOT be grouped with the previous, so we flush the buffer
+
+ Parcel p = Parcel.obtain();
+ p.writeStringList(uids);
+ byte[] d = p.marshall();
+ p.recycle();
+
+ matrix.addRow(new Object[]{
+ lastMasterKeyId, d, header ? 1 : 0
+ });
+ // indicate that we have a header for this masterKeyId
+ header = false;
+
+ // Now clear the buffer, and add the new user id, for the next round
+ uids.clear();
+
+ }
+
+ // 2. This name should be grouped with the previous, just add to buffer
+ uids.add(userId);
+ lastMasterKeyId = masterKeyId;
+
+ // If this one wasn't grouped, the next one's gotta be a header
+ if (!grouped) {
+ header = true;
+ }
+
+ // Regardless of the outcome, move to next entry
+ data.moveToNext();
+
+ }
+
+ // If there is anything left in the buffer, flush it one last time
+ if (!uids.isEmpty()) {
+
+ Parcel p = Parcel.obtain();
+ p.writeStringList(uids);
+ byte[] d = p.marshall();
+ p.recycle();
+
+ matrix.addRow(new Object[]{
+ lastMasterKeyId, d, header ? 1 : 0
+ });
+
+ }
+
+ mUserIdsAdapter.swapCursor(matrix);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mUserIdsAdapter.swapCursor(null);
+ }
+
+ public void setCheckboxVisibility(boolean checkboxVisibility) {
+ this.checkboxVisibility = checkboxVisibility;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
index ea70cde2a..4fd327c8f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
@@ -19,8 +19,6 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.List;
-
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -49,6 +47,7 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.KeychainApplication;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
@@ -59,6 +58,8 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+import java.util.List;
+
public class SettingsActivity extends AppCompatPreferenceActivity {
public static final int REQUEST_CODE_KEYSERVER_PREF = 0x00007005;
@@ -405,7 +406,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
}
/**
- * This fragment shows the keyserver/contacts sync preferences
+ * This fragment shows the keyserver/wifi-only-sync/contacts sync preferences
*/
public static class SyncPrefsFragment extends PresetPreferenceFragment {
@@ -422,8 +423,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
super.onResume();
// this needs to be done in onResume since the user can change sync values from Android
// settings and we need to reflect that change when the user navigates back
- AccountManager manager = AccountManager.get(getActivity());
- final Account account = manager.getAccountsByType(Constants.ACCOUNT_TYPE)[0];
+ final Account account = KeychainApplication.createAccountIfNecessary(getActivity());
// for keyserver sync
initializeSyncCheckBox(
(SwitchPreference) findPreference(Constants.Pref.SYNC_KEYSERVER),
@@ -441,8 +441,11 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
private void initializeSyncCheckBox(final SwitchPreference syncCheckBox,
final Account account,
final String authority) {
- boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority)
- && checkContactsPermission(authority);
+ // account is null if it could not be created for some reason
+ boolean syncEnabled =
+ account != null
+ && ContentResolver.getSyncAutomatically(account, authority)
+ && checkContactsPermission(authority);
syncCheckBox.setChecked(syncEnabled);
setSummary(syncCheckBox, authority, syncEnabled);
@@ -464,6 +467,11 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
return false;
}
} else {
+ if (account == null) {
+ // if account could not be created for some reason,
+ // we can't have our sync
+ return false;
+ }
// disable syncs
ContentResolver.setSyncAutomatically(account, authority, false);
// immediately delete any linked contacts
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 f38e4928d..306b022c1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -31,10 +31,7 @@ import android.widget.Spinner;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.UploadResult;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.UploadKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
@@ -53,7 +50,6 @@ public class UploadKeyActivity extends BaseActivity
// CryptoOperationHelper.Callback vars
private String mKeyserver;
- private long mMasterKeyId;
private CryptoOperationHelper<UploadKeyringParcel, UploadResult> mUploadOpHelper;
@Override
@@ -63,6 +59,10 @@ public class UploadKeyActivity extends BaseActivity
mUploadButton = findViewById(R.id.upload_key_action_upload);
mKeyServerSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
+ MultiUserIdsFragment mMultiUserIdsFragment = (MultiUserIdsFragment)
+ getSupportFragmentManager().findFragmentById(R.id.multi_user_ids_fragment);
+ mMultiUserIdsFragment.setCheckboxVisibility(false);
+
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
.getKeyServers()
@@ -89,15 +89,6 @@ public class UploadKeyActivity extends BaseActivity
return;
}
- try {
- mMasterKeyId = new ProviderHelper(this).getCachedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingUri(mDataUri)).getMasterKeyId();
- } catch (PgpKeyNotFoundException e) {
- Log.e(Constants.TAG, "Intent data pointed to bad key!");
- finish();
- return;
- }
-
}
@Override
@@ -136,7 +127,9 @@ public class UploadKeyActivity extends BaseActivity
@Override
public UploadKeyringParcel createOperationInput() {
- return new UploadKeyringParcel(mKeyserver, mMasterKeyId);
+ long[] masterKeyIds = getIntent().getLongArrayExtra(MultiUserIdsFragment.EXTRA_KEY_IDS);
+
+ return new UploadKeyringParcel(mKeyserver, masterKeyIds[0]);
}
@Override
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 03fc07936..d81797454 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -32,6 +32,8 @@ import android.app.ActivityOptions;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.AsyncTask;
@@ -469,8 +471,7 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
// use new passphrase!
mSaveKeyringParcel.mNewUnlock = new SaveKeyringParcel.ChangeUnlockParcel(
- (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
- null
+ (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE)
);
mEditOpHelper.cryptoOperation();
@@ -924,6 +925,7 @@ public class ViewKeyActivity extends BaseSecurityTokenNfcActivity implements
}
mPhoto.setImageBitmap(photo);
+ mPhoto.setColorFilter(getResources().getColor(R.color.toolbar_photo_tint), PorterDuff.Mode.SRC_ATOP);
mPhotoLayout.setVisibility(View.VISIBLE);
}
};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
index ce2f2def8..02eae1b2b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
@@ -238,7 +238,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
// let user choose application
Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.setType("text/plain");
+ sendIntent.setType(Constants.MIME_TYPE_KEYS);
// NOTE: Don't use Intent.EXTRA_TEXT to send the key
// better send it via a Uri!
@@ -455,8 +455,19 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
}
private void uploadToKeyserver() {
+ long keyId;
+ try {
+ keyId = new ProviderHelper(getActivity())
+ .getCachedPublicKeyRing(mDataUri)
+ .extractOrGetMasterKeyId();
+ } catch (PgpKeyNotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ Notify.create(getActivity(), "key not found", Style.ERROR).show();
+ return;
+ }
Intent uploadIntent = new Intent(getActivity(), UploadKeyActivity.class);
uploadIntent.setData(mDataUri);
+ uploadIntent.putExtra(MultiUserIdsFragment.EXTRA_KEY_IDS, new long[]{keyId});
startActivityForResult(uploadIntent, 0);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
index fb72a263e..4a68c55fe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
@@ -333,14 +333,6 @@ public class KeyAdapter extends CursorAdapter {
return mUserId.email;
}
}
-
- // TODO: workaround for bug in TokenAutoComplete,
- // see https://github.com/open-keychain/open-keychain/issues/1636
- @Override
- public String toString() {
- return " ";
- }
-
}
public static String[] getProjectionWith(String[] projection) {
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 b91abf076..d247faddc 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
@@ -39,6 +39,7 @@ import java.util.ArrayList;
public class MultiUserIdsAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private final ArrayList<Boolean> mCheckStates;
+ private boolean checkboxVisibility = true;
public MultiUserIdsAdapter(Context context, Cursor c, int flags, ArrayList<Boolean> preselectStates) {
super(context, c, flags);
@@ -46,6 +47,11 @@ public class MultiUserIdsAdapter extends CursorAdapter {
mCheckStates = preselectStates == null ? new ArrayList<Boolean>() : preselectStates;
}
+ public MultiUserIdsAdapter(Context context, Cursor c, int flags, ArrayList<Boolean> preselectStates, boolean checkboxVisibility) {
+ this(context,c,flags,preselectStates);
+ this.checkboxVisibility = checkboxVisibility;
+ }
+
@Override
public Cursor swapCursor(Cursor newCursor) {
if (newCursor != null) {
@@ -138,6 +144,7 @@ public class MultiUserIdsAdapter extends CursorAdapter {
}
});
vCheckBox.setClickable(false);
+ vCheckBox.setVisibility(checkboxVisibility?View.VISIBLE:View.GONE);
View vUidBody = view.findViewById(R.id.user_id_body);
vUidBody.setClickable(true);
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 8b2481c29..717299b55 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
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.app.Activity;
import android.content.Context;
import android.graphics.Typeface;
+import android.support.v4.app.FragmentActivity;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
@@ -32,6 +33,7 @@ import android.widget.TextView;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.util.Calendar;
@@ -65,10 +67,11 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
public SaveKeyringParcel.SubkeyAdd mModel;
}
+
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_adv_subkey_item, null);
+ convertView = mInflater.inflate(R.layout.view_key_adv_subkey_item, parent, false);
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);
@@ -88,16 +91,8 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
vStatus.setVisibility(View.GONE);
convertView.setTag(holder);
-
- holder.vDelete.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // remove reference model item from adapter (data and notify about change)
- SubkeysAddedAdapter.this.remove(holder.mModel);
- }
- });
-
}
+
final ViewHolder holder = (ViewHolder) convertView.getTag();
// save reference to model item
@@ -113,8 +108,41 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
boolean isMasterKey = mNewKeyring && position == 0;
if (isMasterKey) {
holder.vKeyId.setTypeface(null, Typeface.BOLD);
+ holder.vDelete.setImageResource(R.drawable.ic_change_grey_24dp);
+ holder.vDelete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // swapping out the old master key with newly set master key
+ AddSubkeyDialogFragment addSubkeyDialogFragment =
+ AddSubkeyDialogFragment.newInstance(true);
+ addSubkeyDialogFragment
+ .setOnAlgorithmSelectedListener(
+ new AddSubkeyDialogFragment.OnAlgorithmSelectedListener() {
+ @Override
+ public void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey) {
+ // calculate manually as the provided position variable
+ // is not always accurate
+ int pos = SubkeysAddedAdapter.this.getPosition(holder.mModel);
+ SubkeysAddedAdapter.this.remove(holder.mModel);
+ SubkeysAddedAdapter.this.insert(newSubkey, pos);
+ }
+ }
+ );
+ addSubkeyDialogFragment.show(
+ ((FragmentActivity)mActivity).getSupportFragmentManager()
+ , "addSubkeyDialog");
+ }
+ });
} else {
holder.vKeyId.setTypeface(null, Typeface.NORMAL);
+ holder.vDelete.setImageResource(R.drawable.ic_close_grey_24dp);
+ holder.vDelete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // remove reference model item from adapter (data and notify about change)
+ SubkeysAddedAdapter.this.remove(holder.mModel);
+ }
+ });
}
holder.vKeyId.setText(R.string.edit_key_new_subkey);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java
index beaf68372..063181dfe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java
@@ -26,6 +26,7 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -65,6 +66,16 @@ public abstract class BaseActivity extends AppCompatActivity {
}
}
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home :
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
public static void onResumeChecks(Context context) {
KeyserverSyncAdapterService.cancelUpdates(context);
// in case user has disabled sync from Android account settings
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 5b75723fb..ce1665382 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
@@ -17,25 +17,26 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import android.annotation.TargetApi;
+import android.annotation.SuppressLint;
import android.app.Dialog;
-import android.os.Build;
+import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AlertDialog;
-import android.text.Editable;
-import android.text.TextWatcher;
+import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.inputmethod.InputMethodManager;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
-import android.widget.EditText;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TableRow;
import android.widget.TextView;
@@ -49,14 +50,18 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.util.Choice;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Calendar;
+import java.util.List;
import java.util.TimeZone;
public class AddSubkeyDialogFragment extends DialogFragment {
public interface OnAlgorithmSelectedListener {
- public void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey);
+ void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey);
+ }
+
+ public enum SupportedKeyType {
+ RSA_2048, RSA_3072, RSA_4096, ECC_P256, ECC_P521
}
private static final String ARG_WILL_BE_MASTER_KEY = "will_be_master_key";
@@ -66,18 +71,12 @@ public class AddSubkeyDialogFragment extends DialogFragment {
private CheckBox mNoExpiryCheckBox;
private TableRow mExpiryRow;
private DatePicker mExpiryDatePicker;
- private Spinner mAlgorithmSpinner;
- private View mKeySizeRow;
- private Spinner mKeySizeSpinner;
- private View mCurveRow;
- private Spinner mCurveSpinner;
- private TextView mCustomKeyTextView;
- private EditText mCustomKeyEditText;
- private TextView mCustomKeyInfoTextView;
- private CheckBox mFlagCertify;
- private CheckBox mFlagSign;
- private CheckBox mFlagEncrypt;
- private CheckBox mFlagAuthenticate;
+ private Spinner mKeyTypeSpinner;
+ private RadioGroup mUsageRadioGroup;
+ private RadioButton mUsageNone;
+ private RadioButton mUsageSign;
+ private RadioButton mUsageEncrypt;
+ private RadioButton mUsageSignAndEncrypt;
private boolean mWillBeMasterKey;
@@ -96,6 +95,8 @@ public class AddSubkeyDialogFragment extends DialogFragment {
return frag;
}
+
+ @NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity context = getActivity();
@@ -106,25 +107,27 @@ public class AddSubkeyDialogFragment extends DialogFragment {
CustomAlertDialogBuilder dialog = new CustomAlertDialogBuilder(context);
+ @SuppressLint("InflateParams")
View view = mInflater.inflate(R.layout.add_subkey_dialog, null);
dialog.setView(view);
- dialog.setTitle(R.string.title_add_subkey);
mNoExpiryCheckBox = (CheckBox) view.findViewById(R.id.add_subkey_no_expiry);
mExpiryRow = (TableRow) view.findViewById(R.id.add_subkey_expiry_row);
mExpiryDatePicker = (DatePicker) view.findViewById(R.id.add_subkey_expiry_date_picker);
- mAlgorithmSpinner = (Spinner) view.findViewById(R.id.add_subkey_algorithm);
- mKeySizeSpinner = (Spinner) view.findViewById(R.id.add_subkey_size);
- mCurveSpinner = (Spinner) view.findViewById(R.id.add_subkey_curve);
- mKeySizeRow = view.findViewById(R.id.add_subkey_row_size);
- mCurveRow = view.findViewById(R.id.add_subkey_row_curve);
- mCustomKeyTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_label);
- mCustomKeyEditText = (EditText) view.findViewById(R.id.add_subkey_custom_key_size_input);
- mCustomKeyInfoTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_info);
- mFlagCertify = (CheckBox) view.findViewById(R.id.add_subkey_flag_certify);
- mFlagSign = (CheckBox) view.findViewById(R.id.add_subkey_flag_sign);
- mFlagEncrypt = (CheckBox) view.findViewById(R.id.add_subkey_flag_encrypt);
- mFlagAuthenticate = (CheckBox) view.findViewById(R.id.add_subkey_flag_authenticate);
+ mKeyTypeSpinner = (Spinner) view.findViewById(R.id.add_subkey_type);
+ mUsageRadioGroup = (RadioGroup) view.findViewById(R.id.add_subkey_usage_group);
+ mUsageNone = (RadioButton) view.findViewById(R.id.add_subkey_usage_none);
+ mUsageSign = (RadioButton) view.findViewById(R.id.add_subkey_usage_sign);
+ mUsageEncrypt = (RadioButton) view.findViewById(R.id.add_subkey_usage_encrypt);
+ mUsageSignAndEncrypt = (RadioButton) view.findViewById(R.id.add_subkey_usage_sign_and_encrypt);
+
+ if(mWillBeMasterKey) {
+ dialog.setTitle(R.string.title_change_master_key);
+ mUsageNone.setVisibility(View.VISIBLE);
+ mUsageNone.setChecked(true);
+ } else {
+ dialog.setTitle(R.string.title_add_subkey);
+ }
mNoExpiryCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
@@ -143,65 +146,24 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mExpiryDatePicker.setMinDate(minDateCal.getTime().getTime());
{
- ArrayList<Choice<Algorithm>> choices = new ArrayList<>();
- choices.add(new Choice<>(Algorithm.DSA, getResources().getString(
- R.string.dsa)));
- if (!mWillBeMasterKey) {
- choices.add(new Choice<>(Algorithm.ELGAMAL, getResources().getString(
- R.string.elgamal)));
- }
- choices.add(new Choice<>(Algorithm.RSA, getResources().getString(
- R.string.rsa)));
- choices.add(new Choice<>(Algorithm.ECDSA, getResources().getString(
- R.string.ecdsa)));
- choices.add(new Choice<>(Algorithm.ECDH, getResources().getString(
- R.string.ecdh)));
- ArrayAdapter<Choice<Algorithm>> adapter = new ArrayAdapter<>(context,
+ ArrayList<Choice<SupportedKeyType>> choices = new ArrayList<>();
+ choices.add(new Choice<>(SupportedKeyType.RSA_2048, getResources().getString(
+ R.string.rsa_2048), getResources().getString(R.string.rsa_2048_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.RSA_3072, getResources().getString(
+ R.string.rsa_3072), getResources().getString(R.string.rsa_3072_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.RSA_4096, getResources().getString(
+ R.string.rsa_4096), getResources().getString(R.string.rsa_4096_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.ECC_P256, getResources().getString(
+ R.string.ecc_p256), getResources().getString(R.string.ecc_p256_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.ECC_P521, getResources().getString(
+ R.string.ecc_p521), getResources().getString(R.string.ecc_p521_description_html)));
+ TwoLineArrayAdapter adapter = new TwoLineArrayAdapter(context,
android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mAlgorithmSpinner.setAdapter(adapter);
- // make RSA the default
+ mKeyTypeSpinner.setAdapter(adapter);
+ // make RSA 3072 the default
for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == Algorithm.RSA) {
- mAlgorithmSpinner.setSelection(i);
- break;
- }
- }
- }
-
- // dynamic ArrayAdapter must be created (instead of ArrayAdapter.getFromResource), because it's content may change
- 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<>();
-
- choices.add(new Choice<>(Curve.NIST_P256, getResources().getString(
- R.string.key_curve_nist_p256)));
- choices.add(new Choice<>(Curve.NIST_P384, getResources().getString(
- R.string.key_curve_nist_p384)));
- choices.add(new Choice<>(Curve.NIST_P521, getResources().getString(
- R.string.key_curve_nist_p521)));
-
- /* @see SaveKeyringParcel
- choices.add(new Choice<Curve>(Curve.BRAINPOOL_P256, getResources().getString(
- R.string.key_curve_bp_p256)));
- choices.add(new Choice<Curve>(Curve.BRAINPOOL_P384, getResources().getString(
- R.string.key_curve_bp_p384)));
- choices.add(new Choice<Curve>(Curve.BRAINPOOL_P512, getResources().getString(
- R.string.key_curve_bp_p512)));
- */
-
- ArrayAdapter<Choice<Curve>> adapter = new ArrayAdapter<>(context,
- android.R.layout.simple_spinner_item, choices);
- mCurveSpinner.setAdapter(adapter);
- // make NIST P-256 the default
- for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == Curve.NIST_P256) {
- mCurveSpinner.setSelection(i);
+ if (choices.get(i).getId() == SupportedKeyType.RSA_3072) {
+ mKeyTypeSpinner.setSelection(i);
break;
}
}
@@ -215,45 +177,35 @@ public class AddSubkeyDialogFragment extends DialogFragment {
final AlertDialog alertDialog = dialog.show();
- mCustomKeyEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
+ mKeyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
- public void afterTextChanged(Editable s) {
- setOkButtonAvailability(alertDialog);
- }
- });
-
- mKeySizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- setCustomKeyVisibility();
- setOkButtonAvailability(alertDialog);
- }
+ // noinspection unchecked
+ SupportedKeyType keyType = ((Choice<SupportedKeyType>) parent.getSelectedItem()).getId();
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
+ // RadioGroup.getCheckedRadioButtonId() gives the wrong RadioButton checked
+ // when programmatically unchecking children radio buttons. Clearing all is the only option.
+ mUsageRadioGroup.clearCheck();
- mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- updateUiForAlgorithm(((Choice<Algorithm>) parent.getSelectedItem()).getId());
+ if(mWillBeMasterKey) {
+ mUsageNone.setChecked(true);
+ }
- setCustomKeyVisibility();
- setOkButtonAvailability(alertDialog);
+ if (keyType == SupportedKeyType.ECC_P521 || keyType == SupportedKeyType.ECC_P256) {
+ mUsageSignAndEncrypt.setEnabled(false);
+ if (mWillBeMasterKey) {
+ mUsageEncrypt.setEnabled(false);
+ }
+ } else {
+ // need to enable if previously disabled for ECC masterkey
+ mUsageEncrypt.setEnabled(true);
+ mUsageSignAndEncrypt.setEnabled(true);
+ }
}
@Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
+ public void onNothingSelected(AdapterView<?> parent) {}
});
return alertDialog;
@@ -269,36 +221,74 @@ public class AddSubkeyDialogFragment extends DialogFragment {
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (!mFlagCertify.isChecked() && !mFlagSign.isChecked()
- && !mFlagEncrypt.isChecked() && !mFlagAuthenticate.isChecked()) {
- Toast.makeText(getActivity(), R.string.edit_key_select_flag, Toast.LENGTH_LONG).show();
+ if (mUsageRadioGroup.getCheckedRadioButtonId() == -1) {
+ Toast.makeText(getActivity(), R.string.edit_key_select_usage, Toast.LENGTH_LONG).show();
return;
}
- Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId();
+ // noinspection unchecked
+ SupportedKeyType keyType = ((Choice<SupportedKeyType>) mKeyTypeSpinner.getSelectedItem()).getId();
Curve curve = null;
Integer keySize = null;
- // For EC keys, add a curve
- if (algorithm == Algorithm.ECDH || algorithm == Algorithm.ECDSA) {
- curve = ((Choice<Curve>) mCurveSpinner.getSelectedItem()).getId();
- // Otherwise, get a keysize
- } else {
- keySize = getProperKeyLength(algorithm, getSelectedKeyLength());
+ Algorithm algorithm = null;
+
+ // set keysize & curve, for RSA & ECC respectively
+ switch (keyType) {
+ case RSA_2048: {
+ keySize = 2048;
+ break;
+ }
+ case RSA_3072: {
+ keySize = 3072;
+ break;
+ }
+ case RSA_4096: {
+ keySize = 4096;
+ break;
+ }
+ case ECC_P256: {
+ curve = Curve.NIST_P256;
+ break;
+ }
+ case ECC_P521: {
+ curve = Curve.NIST_P521;
+ break;
+ }
}
+ // set algorithm
+ switch (keyType) {
+ case RSA_2048:
+ case RSA_3072:
+ case RSA_4096: {
+ algorithm = Algorithm.RSA;
+ break;
+ }
+
+ case ECC_P256:
+ case ECC_P521: {
+ if(mUsageEncrypt.isChecked()) {
+ algorithm = Algorithm.ECDH;
+ } else {
+ algorithm = Algorithm.ECDSA;
+ }
+ break;
+ }
+ }
+
+ // set flags
int flags = 0;
- if (mFlagCertify.isChecked()) {
+ if (mWillBeMasterKey) {
flags |= KeyFlags.CERTIFY_OTHER;
}
- if (mFlagSign.isChecked()) {
+ if (mUsageSign.isChecked()) {
flags |= KeyFlags.SIGN_DATA;
- }
- if (mFlagEncrypt.isChecked()) {
+ } else if (mUsageEncrypt.isChecked()) {
flags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
+ } else if (mUsageSignAndEncrypt.isChecked()) {
+ flags |= KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
}
- if (mFlagAuthenticate.isChecked()) {
- flags |= KeyFlags.AUTHENTICATION;
- }
+
long expiry;
if (mNoExpiryCheckBox.isChecked()) {
@@ -332,206 +322,29 @@ public class AddSubkeyDialogFragment extends DialogFragment {
}
}
- private int getSelectedKeyLength() {
- final String selectedItemString = (String) mKeySizeSpinner.getSelectedItem();
- final String customLengthString = getResources().getString(R.string.key_size_custom);
- final boolean customSelected = customLengthString.equals(selectedItemString);
- String keyLengthString = customSelected ? mCustomKeyEditText.getText().toString() : selectedItemString;
- int keySize;
- try {
- keySize = Integer.parseInt(keyLengthString);
- } catch (NumberFormatException e) {
- keySize = 0;
- }
- return keySize;
- }
-
- /**
- * <h3>RSA</h3>
- * <p>for RSA algorithm, key length must be greater than 2048. Possibility to generate keys bigger
- * than 8192 bits is currently disabled, because it's almost impossible to generate them on a mobile device (check
- * <a href="http://www.javamex.com/tutorials/cryptography/rsa_key_length.shtml">RSA key length plot</a> and
- * <a href="http://www.keylength.com/">Cryptographic Key Length Recommendation</a>). Also, key length must be a
- * multiplicity of 8.</p>
- * <h3>ElGamal</h3>
- * <p>For ElGamal algorithm, supported key lengths are 2048, 3072, 4096 or 8192 bits.</p>
- * <h3>DSA</h3>
- * <p>For DSA algorithm key length must be between 2048 and 3072. Also, it must me dividable by 64.</p>
- *
- * @return correct key length, according to BouncyCastle specification. Returns <code>-1</code>, if key length is
- * inappropriate.
- */
- private int getProperKeyLength(Algorithm algorithm, int currentKeyLength) {
- final int[] elGamalSupportedLengths = {2048, 3072, 4096, 8192};
- int properKeyLength = -1;
- switch (algorithm) {
- case RSA: {
- if (currentKeyLength >= 2048 && currentKeyLength <= 16384) {
- properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8);
- }
- break;
- }
- case ELGAMAL: {
- int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length];
- for (int i = 0; i < elGamalSupportedLengths.length; i++) {
- elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength);
- }
- int minimalValue = Integer.MAX_VALUE;
- int minimalIndex = -1;
- for (int i = 0; i < elGammalKeyDiff.length; i++) {
- if (elGammalKeyDiff[i] <= minimalValue) {
- minimalValue = elGammalKeyDiff[i];
- minimalIndex = i;
- }
- }
- properKeyLength = elGamalSupportedLengths[minimalIndex];
- break;
- }
- case DSA: {
- // Bouncy Castle supports 4096 maximum
- if (currentKeyLength >= 2048 && currentKeyLength <= 4096) {
- properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64);
- }
- break;
- }
+ private class TwoLineArrayAdapter extends ArrayAdapter<Choice<SupportedKeyType>> {
+ public TwoLineArrayAdapter(Context context, int resource, List<Choice<SupportedKeyType>> objects) {
+ super(context, resource, objects);
}
- return properKeyLength;
- }
- private void setOkButtonAvailability(AlertDialog alertDialog) {
- Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId();
- boolean enabled = algorithm == Algorithm.ECDSA || algorithm == Algorithm.ECDH
- || getProperKeyLength(algorithm, getSelectedKeyLength()) > 0;
- alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled);
- }
- private void setCustomKeyVisibility() {
- final String selectedItemString = (String) mKeySizeSpinner.getSelectedItem();
- final String customLengthString = getResources().getString(R.string.key_size_custom);
- final boolean customSelected = customLengthString.equals(selectedItemString);
- final int visibility = customSelected ? View.VISIBLE : View.GONE;
-
- mCustomKeyEditText.setVisibility(visibility);
- mCustomKeyTextView.setVisibility(visibility);
- mCustomKeyInfoTextView.setVisibility(visibility);
-
- // hide keyboard after setting visibility to gone
- if (visibility == View.GONE) {
- InputMethodManager imm = (InputMethodManager)
- getActivity().getSystemService(FragmentActivity.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mCustomKeyEditText.getWindowToken(), 0);
- }
- }
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ // inflate view if not given one
+ if (convertView == null) {
+ convertView = getActivity().getLayoutInflater()
+ .inflate(R.layout.two_line_spinner_dropdown_item, parent, false);
+ }
- private void updateUiForAlgorithm(Algorithm algorithm) {
- final ArrayAdapter<CharSequence> keySizeAdapter = (ArrayAdapter<CharSequence>) mKeySizeSpinner.getAdapter();
- keySizeAdapter.clear();
- switch (algorithm) {
- case RSA: {
- replaceArrayAdapterContent(keySizeAdapter, R.array.rsa_key_size_spinner_values);
- mKeySizeSpinner.setSelection(1);
- mKeySizeRow.setVisibility(View.VISIBLE);
- mCurveRow.setVisibility(View.GONE);
- mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_rsa));
- // allowed flags:
- mFlagSign.setEnabled(true);
- mFlagEncrypt.setEnabled(true);
- mFlagAuthenticate.setEnabled(true);
-
- if (mWillBeMasterKey) {
- mFlagCertify.setEnabled(true);
-
- mFlagCertify.setChecked(true);
- mFlagSign.setChecked(false);
- mFlagEncrypt.setChecked(false);
- } else {
- mFlagCertify.setEnabled(false);
+ Choice c = this.getItem(position);
- mFlagCertify.setChecked(false);
- mFlagSign.setChecked(true);
- mFlagEncrypt.setChecked(true);
- }
- mFlagAuthenticate.setChecked(false);
- break;
- }
- case ELGAMAL: {
- replaceArrayAdapterContent(keySizeAdapter, R.array.elgamal_key_size_spinner_values);
- mKeySizeSpinner.setSelection(3);
- mKeySizeRow.setVisibility(View.VISIBLE);
- mCurveRow.setVisibility(View.GONE);
- mCustomKeyInfoTextView.setText(""); // ElGamal does not support custom key length
- // allowed flags:
- mFlagCertify.setChecked(false);
- mFlagCertify.setEnabled(false);
- mFlagSign.setChecked(false);
- mFlagSign.setEnabled(false);
- mFlagEncrypt.setChecked(true);
- mFlagEncrypt.setEnabled(true);
- mFlagAuthenticate.setChecked(false);
- mFlagAuthenticate.setEnabled(false);
- break;
- }
- case DSA: {
- replaceArrayAdapterContent(keySizeAdapter, R.array.dsa_key_size_spinner_values);
- mKeySizeSpinner.setSelection(2);
- mKeySizeRow.setVisibility(View.VISIBLE);
- mCurveRow.setVisibility(View.GONE);
- mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_dsa));
- // allowed flags:
- mFlagCertify.setChecked(false);
- mFlagCertify.setEnabled(false);
- mFlagSign.setChecked(true);
- mFlagSign.setEnabled(true);
- mFlagEncrypt.setChecked(false);
- mFlagEncrypt.setEnabled(false);
- mFlagAuthenticate.setChecked(false);
- mFlagAuthenticate.setEnabled(false);
- break;
- }
- case ECDSA: {
- mKeySizeRow.setVisibility(View.GONE);
- mCurveRow.setVisibility(View.VISIBLE);
- mCustomKeyInfoTextView.setText("");
- // allowed flags:
- mFlagCertify.setEnabled(mWillBeMasterKey);
- mFlagCertify.setChecked(mWillBeMasterKey);
- mFlagSign.setEnabled(true);
- mFlagSign.setChecked(!mWillBeMasterKey);
- mFlagEncrypt.setEnabled(false);
- mFlagEncrypt.setChecked(false);
- mFlagAuthenticate.setEnabled(true);
- mFlagAuthenticate.setChecked(false);
- break;
- }
- case ECDH: {
- mKeySizeRow.setVisibility(View.GONE);
- mCurveRow.setVisibility(View.VISIBLE);
- mCustomKeyInfoTextView.setText("");
- // allowed flags:
- mFlagCertify.setChecked(false);
- mFlagCertify.setEnabled(false);
- mFlagSign.setChecked(false);
- mFlagSign.setEnabled(false);
- mFlagEncrypt.setChecked(true);
- mFlagEncrypt.setEnabled(true);
- mFlagAuthenticate.setChecked(false);
- mFlagAuthenticate.setEnabled(false);
- break;
- }
- }
- keySizeAdapter.notifyDataSetChanged();
+ TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
+ TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
- }
+ text1.setText(c.getName());
+ text2.setText(Html.fromHtml(c.getDescription()));
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- private void replaceArrayAdapterContent(ArrayAdapter<CharSequence> arrayAdapter, int stringArrayResourceId) {
- final String[] spinnerValuesStringArray = getResources().getStringArray(stringArrayResourceId);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- arrayAdapter.addAll(spinnerValuesStringArray);
- } else {
- for (final String value : spinnerValuesStringArray) {
- arrayAdapter.add(value);
- }
+ return convertView;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
index b51648740..9f5ffc358 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
@@ -36,6 +36,10 @@ public class EditSubkeyDialogFragment extends DialogFragment {
public static final int MESSAGE_REVOKE = 2;
public static final int MESSAGE_STRIP = 3;
public static final int MESSAGE_MOVE_KEY_TO_CARD = 4;
+ public static final int SUBKEY_MENU_CHANGE_EXPIRY = 0;
+ public static final int SUBKEY_MENU_REVOKE_SUBKEY = 1;
+ public static final int SUBKEY_MENU_STRIP_SUBKEY = 2;
+ public static final int SUBKEY_MENU_MOVE_TO_SECURITY_TOKEN = 3;
private Messenger mMessenger;
@@ -68,16 +72,16 @@ public class EditSubkeyDialogFragment extends DialogFragment {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
- case 0:
+ case SUBKEY_MENU_CHANGE_EXPIRY:
sendMessageToHandler(MESSAGE_CHANGE_EXPIRY, null);
break;
- case 1:
+ case SUBKEY_MENU_REVOKE_SUBKEY:
sendMessageToHandler(MESSAGE_REVOKE, null);
break;
- case 2:
- sendMessageToHandler(MESSAGE_STRIP, null);
+ case SUBKEY_MENU_STRIP_SUBKEY:
+ showAlertDialog();
break;
- case 3:
+ case SUBKEY_MENU_MOVE_TO_SECURITY_TOKEN:
sendMessageToHandler(MESSAGE_MOVE_KEY_TO_CARD, null);
break;
default:
@@ -95,6 +99,25 @@ public class EditSubkeyDialogFragment extends DialogFragment {
return builder.show();
}
+ private void showAlertDialog() {
+ CustomAlertDialogBuilder stripAlertDialog = new CustomAlertDialogBuilder(getActivity());
+ stripAlertDialog.setTitle(R.string.title_alert_strip).
+ setMessage(R.string.alert_strip).setCancelable(true);
+ stripAlertDialog.setPositiveButton(R.string.strip, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ sendMessageToHandler(MESSAGE_STRIP, null);
+ }
+ });
+ stripAlertDialog.setNegativeButton(R.string.btn_do_not_save, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ dismiss();
+ }
+ });
+ stripAlertDialog.show();
+ }
+
/**
* Send message back to handler which is initialized in a activity
*
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java
new file mode 100644
index 000000000..7919a0918
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 Alex Fong Jie Wen <alexfongg@gmail.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.util.spinner;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Spinner;
+
+/**
+ * Custom spinner which uses a hack to
+ * always set focus on first item in list
+ *
+ */
+public class FocusFirstItemSpinner extends Spinner {
+ /**
+ * Spinner is originally designed to set focus on the currently selected item.
+ * When Spinner is selected to show dropdown, 'performClick()' is called internally.
+ * 'getSelectedItemPosition()' is then called to obtain the item to focus on.
+ * We use a toggle to have 'getSelectedItemPosition()' return the 0th index
+ * for this particular case.
+ */
+
+ private boolean mToggleFlag = true;
+
+ public FocusFirstItemSpinner(Context context, AttributeSet attrs,
+ int defStyle, int mode) {
+ super(context, attrs, defStyle, mode);
+ }
+
+ public FocusFirstItemSpinner(Context context, AttributeSet attrs,
+ int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public FocusFirstItemSpinner(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FocusFirstItemSpinner(Context context, int mode) {
+ super(context, mode);
+ }
+
+ public FocusFirstItemSpinner(Context context) {
+ super(context);
+ }
+
+ @Override
+ public int getSelectedItemPosition() {
+ if (!mToggleFlag) {
+ return 0;
+ }
+ return super.getSelectedItemPosition();
+ }
+
+ @Override
+ public boolean performClick() {
+ mToggleFlag = false;
+ boolean result = super.performClick();
+ mToggleFlag = true;
+ return result;
+ }
+}
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 6a51085f3..63afb1e8b 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
@@ -113,11 +113,6 @@ public class CertifyKeySpinner extends KeySpinner {
@Override
boolean isItemEnabled(Cursor cursor) {
- // "none" entry is always enabled!
- if (cursor.getPosition() == 0) {
- return true;
- }
-
if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) {
return false;
}
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 8fb9e38aa..0c2d93ad9 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
@@ -72,11 +72,6 @@ public class SignKeySpinner extends KeySpinner {
@Override
boolean isItemEnabled(Cursor cursor) {
- // "none" entry is always enabled!
- if (cursor.getPosition() == 0) {
- return true;
- }
-
if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) {
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
index 48f10d4b9..5ffce9f24 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
@@ -18,12 +18,15 @@
package org.sufficientlysecure.keychain.util;
public class Choice <E> {
+
private String mName;
private E mId;
+ private String mDescription;
- public Choice(E id, String name) {
+ public Choice(E id, String name, String description) {
mId = id;
mName = name;
+ mDescription = description;
}
public E getId() {
@@ -34,6 +37,10 @@ public class Choice <E> {
return mName;
}
+ public String getDescription() {
+ return mDescription;
+ }
+
@Override
public String toString() {
return mName;
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 b3d679a0e..5f53845d8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
@@ -19,20 +19,9 @@
package org.sufficientlysecure.keychain.util;
-import java.io.Serializable;
-import java.net.Proxy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-
+import android.accounts.Account;
import android.annotation.SuppressLint;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Parcel;
@@ -42,9 +31,23 @@ import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.Pref;
+import org.sufficientlysecure.keychain.KeychainApplication;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService;
+import java.io.Serializable;
+import java.net.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
/**
* Singleton Implementation of a Preference Helper
*/
@@ -76,9 +79,8 @@ public class Preferences {
/**
* Makes android's preference framework write to our file instead of default.
- * This allows us to use the "persistent" attribute to simplify code, which automatically
+ * This allows us to use the xml "persistent" attribute to simplify code, which automatically
* writes and reads preference values.
- * @param manager
*/
public static void setPreferenceManagerFileAndMode(PreferenceManager manager) {
manager.setSharedPreferencesName(PREF_FILE_NAME);
@@ -302,6 +304,23 @@ public class Preferences {
}
+ /**
+ * @return true if a periodic sync exists and is set to run automatically, false otherwise
+ */
+ public static boolean getKeyserverSyncEnabled(Context context) {
+ Account account = KeychainApplication.createAccountIfNecessary(context);
+
+ if (account == null) {
+ // if the account could not be created for some reason, we can't have a sync
+ return false;
+ }
+
+ String authority = Constants.PROVIDER_AUTHORITY;
+
+ return ContentResolver.getSyncAutomatically(account, authority) &&
+ !ContentResolver.getPeriodicSyncs(account, authority).isEmpty();
+ }
+
public CacheTTLPrefs getPassphraseCacheTtl() {
Set<String> pref = mSharedPreferences.getStringSet(Constants.Pref.PASSPHRASE_CACHE_TTLS, null);
if (pref == null) {
@@ -424,6 +443,12 @@ public class Preferences {
};
}
+ // sync preferences
+
+ public boolean getWifiOnlySync() {
+ return mSharedPreferences.getBoolean(Pref.ENABLE_WIFI_SYNC_ONLY, true);
+ }
+
// experimental prefs
public boolean getExperimentalEnableWordConfirm() {
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..f625ba425
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..6cf9d044a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..1c30b6f22
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..672a9c96c
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.png
new file mode 100644
index 000000000..5be101001
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml
index 4b5058a81..b232ed423 100644
--- a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml
+++ b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml
@@ -13,147 +13,68 @@
android:paddingRight="24dp"
android:stretchColumns="1">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="4dp"
- android:text="@string/key_creation_el_gamal_info" />
-
<TableRow>
-
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:text="@string/label_algorithm" />
+ android:paddingRight="10dp"
+ android:text="@string/label_key_type" />
- <Spinner
- android:id="@+id/add_subkey_algorithm"
+ <!-- custom spinner for fixing focus on first item in list at all times -->
+ <org.sufficientlysecure.keychain.ui.util.spinner.FocusFirstItemSpinner
+ android:id="@+id/add_subkey_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="4dp" />
- </TableRow>
-
- <TableRow android:id="@+id/add_subkey_row_size">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_key_size" />
-
- <Spinner
- android:id="@+id/add_subkey_size"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="right"
+ android:dropDownWidth="wrap_content"
android:padding="4dp" />
</TableRow>
<TableRow
- android:id="@+id/add_subkey_row_curve"
- android:visibility="gone">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_ecc_curve"/>
-
- <Spinner
- android:id="@+id/add_subkey_curve"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="right"
- android:padding="4dp"/>
- </TableRow>
-
- <TextView
- android:id="@+id/add_subkey_custom_key_size_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/key_size_custom_info"
- android:visibility="gone" />
-
- <EditText
- android:id="@+id/add_subkey_custom_key_size_input"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:inputType="number"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/add_subkey_custom_key_size_info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
-
- <TableRow>
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp">
<TextView
android:id="@+id/label_usage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
+ android:layout_gravity="center_vertical|top"
+ android:paddingRight="10dp"
android:text="@string/label_usage" />
-
- <CheckBox
- android:id="@+id/add_subkey_flag_certify"
- android:enabled="false"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/flag_certify" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip" />
-
- <CheckBox
- android:id="@+id/add_subkey_flag_sign"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/flag_sign" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip" />
-
- <CheckBox
- android:id="@+id/add_subkey_flag_encrypt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/flag_encrypt" />
+ <RadioGroup
+ android:id="@+id/add_subkey_usage_group"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+
+ <RadioButton
+ android:id="@+id/add_subkey_usage_none"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:text="@string/usage_none" />
+
+ <RadioButton
+ android:id="@+id/add_subkey_usage_sign"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/usage_sign" />
+
+ <RadioButton
+ android:id="@+id/add_subkey_usage_encrypt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/usage_encrypt" />
+
+ <RadioButton
+ android:id="@+id/add_subkey_usage_sign_and_encrypt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/usage_sign_and_encrypt" />
+ </RadioGroup>
</TableRow>
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip" />
-
- <CheckBox
- android:id="@+id/add_subkey_flag_authenticate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/flag_authenticate" />
- </TableRow>
<TableRow
android:layout_marginTop="8dp"
@@ -164,7 +85,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
+ android:paddingRight="10dp"
android:text="@string/label_expiry" />
<CheckBox
diff --git a/OpenKeychain/src/main/res/layout/certify_item.xml b/OpenKeychain/src/main/res/layout/certify_item.xml
index 71838c2fc..8aff5823e 100644
--- a/OpenKeychain/src/main/res/layout/certify_item.xml
+++ b/OpenKeychain/src/main/res/layout/certify_item.xml
@@ -13,7 +13,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:clickable="true"
- android:layout_marginLeft="8dip"
android:layout_marginTop="8dip"/>
<LinearLayout android:id="@+id/user_id_body"
@@ -21,7 +20,6 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:singleLine="true"
- android:layout_marginLeft="8dip"
android:layout_marginTop="4dip">
<CheckBox
diff --git a/OpenKeychain/src/main/res/layout/certify_key_fragment.xml b/OpenKeychain/src/main/res/layout/certify_key_fragment.xml
index 23685ce15..01837240b 100644
--- a/OpenKeychain/src/main/res/layout/certify_key_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/certify_key_fragment.xml
@@ -22,8 +22,9 @@
android:id="@+id/textView"
android:layout_weight="1" />
- <org.sufficientlysecure.keychain.ui.widget.FixedListView
- android:id="@+id/view_key_user_ids"
+ <fragment
+ android:id="@+id/multi_user_ids_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.MultiUserIdsFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
diff --git a/OpenKeychain/src/main/res/layout/multi_user_ids_fragment.xml b/OpenKeychain/src/main/res/layout/multi_user_ids_fragment.xml
new file mode 100644
index 000000000..560934f50
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/multi_user_ids_fragment.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<org.sufficientlysecure.keychain.ui.widget.FixedListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/view_key_user_ids"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
diff --git a/OpenKeychain/src/main/res/layout/two_line_spinner_dropdown_item.xml b/OpenKeychain/src/main/res/layout/two_line_spinner_dropdown_item.xml
new file mode 100644
index 000000000..ae325eaab
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/two_line_spinner_dropdown_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingLeft="10dp"
+ android:paddingTop="6dp"
+ android:paddingRight="10dp"
+ android:paddingBottom="6dp"
+ android:background="?android:attr/selectableItemBackground">
+
+ <TextView android:id="@android:id/text1"
+ style="?android:attr/spinnerDropDownItemStyle"
+ android:textStyle="bold"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp" />
+
+ <TextView android:id="@android:id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp"
+ android:paddingTop="2dp" />
+
+</LinearLayout>
+
+
+
+
diff --git a/OpenKeychain/src/main/res/layout/upload_key_activity.xml b/OpenKeychain/src/main/res/layout/upload_key_activity.xml
index 6a0be9ce5..6acb22c3a 100644
--- a/OpenKeychain/src/main/res/layout/upload_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/upload_key_activity.xml
@@ -41,6 +41,12 @@
android:layout_marginBottom="4dp"
android:layout_marginTop="4dp" />
+ <fragment
+ android:id="@+id/multi_user_ids_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.MultiUserIdsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<TextView
style="@style/SectionHeader"
android:layout_width="wrap_content"
diff --git a/OpenKeychain/src/main/res/values/colors.xml b/OpenKeychain/src/main/res/values/colors.xml
index 93cf126f7..d9075b587 100644
--- a/OpenKeychain/src/main/res/values/colors.xml
+++ b/OpenKeychain/src/main/res/values/colors.xml
@@ -35,5 +35,6 @@
<!-- linked ID view -->
<color name="card_view_button">#7bad45</color>
+ <color name="toolbar_photo_tint">#1E7bad45</color>
</resources>
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 70db4029b..d77d64a27 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -13,6 +13,7 @@
<string name="title_encrypt_files">"Encrypt"</string>
<string name="title_decrypt">"Decrypt"</string>
<string name="title_add_subkey">"Add subkey"</string>
+ <string name="title_change_master_key">Change master key</string>
<string name="title_edit_key">"Edit Key"</string>
<string name="title_linked_create">"Create a Linked Identity"</string>
<string name="title_preferences">"Settings"</string>
@@ -40,6 +41,7 @@
<string name="title_advanced_key_info">"Advanced"</string>
<string name="title_delete_secret_key">"Delete YOUR key '%s'?"</string>
<string name="title_manage_my_keys">"Manage my keys"</string>
+ <string name="title_alert_strip">"Strip this subkey"</string>
<!-- section -->
<string name="section_user_ids">"Identities"</string>
@@ -165,6 +167,7 @@
<string name="label_keyservers">"Select OpenPGP keyservers"</string>
<string name="label_key_id">"Key ID"</string>
<string name="label_key_created">"Key created %s"</string>
+ <string name="label_key_type">"Type"</string>
<string name="label_creation">"Creation"</string>
<string name="label_expiry">"Expiry"</string>
<string name="label_usage">"Usage"</string>
@@ -201,6 +204,7 @@
<string name="label_sync_settings_keyserver_title">"Automatic key updates"</string>
<string name="label_sync_settings_keyserver_summary_on">"Every three days, keys are updated from the preferred keyserver"</string>
<string name="label_sync_settings_keyserver_summary_off">"Keys are not automatically updated"</string>
+ <string name="label_sync_settings_wifi_title">"Sync only on Wi-Fi"</string>
<string name="label_sync_settings_contacts_title">"Link keys to contacts"</string>
<string name="label_sync_settings_contacts_summary_on">"Link keys to contacts based on names and email addresses. This happens completely offline on your device."</string>
<string name="label_sync_settings_contacts_summary_off">"New keys will not be linked to contacts"</string>
@@ -281,22 +285,26 @@
<string name="choice_8hours">"8 hours"</string>
<string name="choice_forever">"forever"</string>
<string name="choice_select_cert">"Select a Key"</string>
- <string name="dsa">"DSA"</string>
- <string name="elgamal">"ElGamal"</string>
- <string name="rsa">"RSA"</string>
- <string name="ecdh">"ECDH"</string>
- <string name="ecdsa">"ECDSA"</string>
<string name="filemanager_title_open">"Open…"</string>
+ <string name="rsa_2048">"RSA 2048"</string>
+ <string name="rsa_2048_description_html">"smaller filesize, considered secure until 2030"</string>
+ <string name="rsa_3072">"RSA 3072"</string>
+ <string name="rsa_3072_description_html">"recommended, considered secure until 2040"</string>
+ <string name="rsa_4096">"RSA 4096"</string>
+ <string name="rsa_4096_description_html">"larger file size, considered secure until 2040+"</string>
+ <string name="ecc_p256">"ECC P-256"</string>
+ <string name="ecc_p256_description_html">"very tiny filesize, considered secure until 2040 &lt;br/> &lt;u>experimental and not supported by all implementations&lt;/u>"</string>
+ <string name="ecc_p521">"ECC P-521"</string>
+ <string name="ecc_p521_description_html">"tiny filesize, considered secure until 2040+ &lt;br/> &lt;u>experimental and not supported by all implementations"&lt;/u></string>
+ <string name="usage_none">"None (subkey binding only)"</string>
+ <string name="usage_sign">"Sign"</string>
+ <string name="usage_encrypt">"Encrypt"</string>
+ <string name="usage_sign_and_encrypt">"Sign &amp; Encrypt"</string>
<string name="error">"Error"</string>
<string name="error_message">"Error: %s"</string>
<string name="theme_dark">"Dark"</string>
<string name="theme_light">"Light"</string>
-
- <!-- key flags -->
- <string name="flag_certify">"Certify"</string>
- <string name="flag_sign">"Sign"</string>
- <string name="flag_encrypt">"Encrypt"</string>
- <string name="flag_authenticate">"Authenticate"</string>
+ <string name="strip">"Strip it"</string>
<!-- sentences -->
<string name="wrong_passphrase">"Wrong password."</string>
@@ -331,6 +339,7 @@
<string name="public_key_deletetion_confirmation">"Delete key '%s'?"</string>
<string name="also_export_secret_keys">"Also export secret keys"</string>
<string name="reinstall_openkeychain">"You encountered a known bug with Android. Please reinstall OpenKeychain if you want to link your contacts with keys."</string>
+ <string name="alert_strip">"Stripping this subkey will make it unusable on this device!"</string>
<string name="key_exported">"Successfully exported 1 key."</string>
<string name="keys_exported">"Successfully exported %d keys."</string>
@@ -746,7 +755,7 @@
<item>"Move Subkey to Security Token"</item>
</string-array>
<string name="edit_key_new_subkey">"new subkey"</string>
- <string name="edit_key_select_flag">"Please select at least one flag!"</string>
+ <string name="edit_key_select_usage">"Please select key usage!"</string>
<string name="edit_key_error_add_identity">"Add at least one identity!"</string>
<string name="edit_key_error_add_subkey">"Add at least one subkey!"</string>
<string name="edit_key_error_bad_security_token_algo">"Algorithm not supported by Security Token!"</string>
diff --git a/OpenKeychain/src/main/res/xml/sync_preferences.xml b/OpenKeychain/src/main/res/xml/sync_preferences.xml
index de41ff030..600ccc9e8 100644
--- a/OpenKeychain/src/main/res/xml/sync_preferences.xml
+++ b/OpenKeychain/src/main/res/xml/sync_preferences.xml
@@ -4,6 +4,12 @@
android:persistent="false"
android:title="@string/label_sync_settings_keyserver_title"/>
<SwitchPreference
+ android:key="enableWifiSyncOnly"
+ android:defaultValue="true"
+ android:persistent="true"
+ android:dependency="syncKeyserver"
+ android:title="@string/label_sync_settings_wifi_title"/>
+ <SwitchPreference
android:key="syncContacts"
android:persistent="false"
android:title="@string/label_sync_settings_contacts_title" />
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
index 10ce0057b..33678ecac 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
@@ -31,6 +31,8 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
+import org.bouncycastle.bcpg.sig.KeyFlags;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -40,8 +42,6 @@ import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
-import org.bouncycastle.bcpg.sig.KeyFlags;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
@@ -124,13 +124,14 @@ public class ExportTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("snails");
- parcel.mNewUnlock = new ChangeUnlockParcel(null, new Passphrase("1234"));
+ parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase("1234"));
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
assertTrue("initial test key creation must succeed", result.success());
Assert.assertNotNull("initial test key creation must succeed", result.getRing());
mStaticRing2 = result.getRing();
+ mStaticRing2 = UncachedKeyRing.forTestingOnlyAddDummyLocalSignature(mStaticRing2, "1234");
}
}
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
index 19fb0345b..666adc0f6 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
@@ -1232,47 +1232,7 @@ public class PgpKeyOperationTest {
}
@Test
- public void testUnlockPin() throws Exception {
-
- Passphrase pin = new Passphrase("5235125");
-
- // change passphrase to a pin type
- parcel.mNewUnlock = new ChangeUnlockParcel(null, pin);
- UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
-
- Assert.assertEquals("exactly three packets should have been added (the secret keys + notation packet)",
- 3, onlyA.size());
- Assert.assertEquals("exactly four packets should have been added (the secret keys + notation packet)",
- 4, onlyB.size());
-
- RawPacket dkSig = onlyB.get(1);
- Assert.assertEquals("second modified packet should be notation data",
- PacketTags.SIGNATURE, dkSig.tag);
-
- // check that notation data contains pin
- CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
- modified.getEncoded(), false, 0);
- Assert.assertEquals("secret key type should be 'pin' after this",
- SecretKeyType.PIN,
- secretRing.getSecretKey().getSecretKeyTypeSuperExpensive());
-
- // need to sleep for a sec, so the timestamp changes for notation data
- Thread.sleep(1000);
-
- {
- parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase("phrayse"), null);
- applyModificationWithChecks(parcel, modified, onlyA, onlyB, new CryptoInputParcel(pin), true, false);
-
- Assert.assertEquals("exactly four packets should have been removed (the secret keys + notation packet)",
- 4, onlyA.size());
- Assert.assertEquals("exactly three packets should have been added (no more notation packet)",
- 3, onlyB.size());
- }
-
- }
-
- @Test
- public void testRestricted () throws Exception {
+ public void testRestricted() throws Exception {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
diff --git a/README.md b/README.md
index d7a07e232..940699fb4 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@ Development mailinglist at https://lists.riseup.net/www/subscribe/openkeychain
3. Have Android SDK "tools", "platform-tools", and "build-tools" directories in your PATH (http://developer.android.com/sdk/index.html)
4. Open the Android SDK Manager (shell command: ``android``).
Expand the Tools directory and select "Android SDK Build-tools (Version 23.0.1)".
-Expand the Extras directory and install "Android Support Repository"
+Expand the Extras directory and install "Android Support Library", as well as "Local Maven repository for Support Libraries"
Select SDK Platform for API levels 21, 22, and 23.
5. Export ANDROID_HOME pointing to your Android SDK
6. Execute ``./gradlew assembleFdroidDebug``
@@ -74,7 +74,7 @@ We are using the newest [Android Studio](http://developer.android.com/sdk/instal
OpenKeychain uses a forked version with some small changes. These changes will been sent to Bouncy Castle.
see
-* Fork: https://github.com/openpgp-keychain/bouncycastle
+* Fork: https://github.com/open-keychain/bouncycastle
#### Bouncy Castle resources