aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main')
-rw-r--r--OpenKeychain/src/main/AndroidManifest.xml35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java149
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java21
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java109
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java560
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/CertifyResult.java151
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/GetKeyResult.java64
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/ImportKeyResult.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java100
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/AddKeysActivity.java463
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java207
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java52
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java27
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java65
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java65
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java64
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java142
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyActivity.java42
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyFragment.java386
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java32
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java183
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/UserIdInfoDialogFragment.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java129
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ExchangeKeySpinner.java100
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java1
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/encrypted_small.pngbin2187 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_action_cloud.pngbin0 -> 450 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_action_search_cloud.pngbin0 -> 1338 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/overlay_error.pngbin1986 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/overlay_ok.pngbin1702 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/revoked_key_small.pngbin2509 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/signed_large.pngbin5928 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/overlay_error.pngbin1192 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/overlay_ok.pngbin1038 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/signed_large.pngbin2611 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_action_cloud.pngbin0 -> 335 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_action_search_cloud.pngbin0 -> 913 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/overlay_error.pngbin1539 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/overlay_ok.pngbin1305 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/signed_large.pngbin3858 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/signed_small.pngbin1576 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_action_cloud.pngbin0 -> 538 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_action_search_cloud.pngbin0 -> 1750 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_cloud.pngbin0 -> 760 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_search_cloud.pngbin0 -> 2560 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable/overlay_error.pngbin1539 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable/overlay_ok.pngbin1305 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable/signed_large.pngbin3858 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/layout/add_keys_activity.xml140
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_input_fragment.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_result_include.xml201
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml146
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_content.xml31
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_item.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/keyspinner_item.xml13
-rw-r--r--OpenKeychain/src/main/res/layout/log_display_fragment.xml11
-rw-r--r--OpenKeychain/src/main/res/layout/log_display_item.xml82
-rw-r--r--OpenKeychain/src/main/res/layout/multi_certify_item.xml88
-rw-r--r--OpenKeychain/src/main/res/layout/multi_certify_key_activity.xml22
-rw-r--r--OpenKeychain/src/main/res/layout/multi_certify_key_fragment.xml131
-rw-r--r--OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml1
-rw-r--r--OpenKeychain/src/main/res/layout/select_key_item.xml20
-rw-r--r--OpenKeychain/src/main/res/layout/select_secret_key_activity.xml12
-rw-r--r--OpenKeychain/src/main/res/layout/select_secret_key_layout_fragment.xml75
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_main_fragment.xml1
-rw-r--r--OpenKeychain/src/main/res/menu/key_list.xml15
-rw-r--r--OpenKeychain/src/main/res/values-cs/strings.xml12
-rw-r--r--OpenKeychain/src/main/res/values-de/strings.xml14
-rw-r--r--OpenKeychain/src/main/res/values-es/strings.xml14
-rw-r--r--OpenKeychain/src/main/res/values-fr/strings.xml14
-rw-r--r--OpenKeychain/src/main/res/values-it/strings.xml12
-rw-r--r--OpenKeychain/src/main/res/values-ja/strings.xml14
-rw-r--r--OpenKeychain/src/main/res/values-nl/strings.xml2
-rw-r--r--OpenKeychain/src/main/res/values-pl/strings.xml2
-rw-r--r--OpenKeychain/src/main/res/values-ru/strings.xml10
-rw-r--r--OpenKeychain/src/main/res/values-sl/strings.xml12
-rw-r--r--OpenKeychain/src/main/res/values-sr/strings.xml14
-rw-r--r--OpenKeychain/src/main/res/values-tr/strings.xml10
-rw-r--r--OpenKeychain/src/main/res/values-uk/strings.xml12
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml119
105 files changed, 3689 insertions, 1076 deletions
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index a00b6f01c..f68499fc8 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -424,6 +424,15 @@
android:value=".ui.ViewKeyActivity" />
</activity>
<activity
+ android:name=".ui.MultiCertifyKeyActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
+ android:label="@string/title_certify_key"
+ android:parentActivityName=".ui.AddKeysActivity">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".ui.KeyListActivity" />
+ </activity>
+ <activity
android:name=".ui.ImportKeysActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_import_keys"
@@ -615,6 +624,16 @@
android:value=".ui.KeyListActivity" />
</activity>
<activity
+ android:name=".ui.AddKeysActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
+ android:label="@string/title_add_keys"
+ android:windowSoftInputMode="stateHidden"
+ android:parentActivityName=".ui.KeyListActivity">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".ui.KeyListActivity" />
+ </activity>
+ <activity
android:name=".ui.ConsolidateDialogActivity"
android:theme="@android:style/Theme.NoDisplay" />
<activity
@@ -634,14 +653,14 @@
android:allowTaskReparenting="true" />
<!--<activity-->
- <!--android:name=".ui.NfcIntentActivity"-->
- <!--android:launchMode="singleTop">-->
- <!--<intent-filter>-->
- <!--<action android:name="android.nfc.action.NDEF_DISCOVERED" />-->
-
- <!--<category android:name="android.intent.category.DEFAULT" />-->
- <!--<data android:host="my.yubico.com" android:scheme="https"/>-->
- <!--</intent-filter>-->
+ <!--android:name=".ui.NfcIntentActivity"-->
+ <!--android:launchMode="singleTop">-->
+ <!--<intent-filter>-->
+ <!--<action android:name="android.nfc.action.NDEF_DISCOVERED" />-->
+
+ <!--<category android:name="android.intent.category.DEFAULT" />-->
+ <!--<data android:host="my.yubico.com" android:scheme="https"/>-->
+ <!--</intent-filter>-->
<!--</activity>-->
<activity
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
index dafa3aeed..854a90713 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
@@ -73,7 +73,10 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
}
dest.writeString(mFingerprintHex);
dest.writeString(mKeyIdHex);
- dest.writeInt(mBitStrength);
+ dest.writeInt(mBitStrength == null ? 0 : 1);
+ if (mBitStrength != null) {
+ dest.writeInt(mBitStrength);
+ }
dest.writeString(mAlgorithm);
dest.writeByte((byte) (mSecretKey ? 1 : 0));
dest.writeByte((byte) (mSelected ? 1 : 0));
@@ -94,7 +97,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.mDate = source.readInt() != 0 ? new Date(source.readLong()) : null;
vr.mFingerprintHex = source.readString();
vr.mKeyIdHex = source.readString();
- vr.mBitStrength = source.readInt();
+ vr.mBitStrength = source.readInt() != 0 ? source.readInt() : null;
vr.mAlgorithm = source.readString();
vr.mSecretKey = source.readByte() == 1;
vr.mSelected = source.readByte() == 1;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
index f43cbbeef..08b7316aa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
@@ -25,6 +25,7 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Date;
/** A generic wrapped PGPKeyRing object.
*
@@ -51,6 +52,10 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
return mVerified;
}
+ public byte[] getFingerprint() {
+ return getRing().getPublicKey().getFingerprint();
+ }
+
public String getPrimaryUserId() throws PgpGeneralException {
return getPublicKey().getPrimaryUserId();
}
@@ -67,11 +72,21 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
return getPublicKey().getUnorderedUserIds();
}
- public boolean isRevoked() throws PgpGeneralException {
+ public boolean isRevoked() {
// Is the master key revoked?
return getRing().getPublicKey().isRevoked();
}
+ public boolean isExpired() {
+ // Is the master key expired?
+ Date creationDate = getRing().getPublicKey().getCreationTime();
+ Date expiryDate = getRing().getPublicKey().getValidSeconds() > 0
+ ? new Date(creationDate.getTime() + getRing().getPublicKey().getValidSeconds() * 1000) : null;
+
+ Date now = new Date();
+ return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
+ }
+
public boolean canCertify() throws PgpGeneralException {
return getRing().getPublicKey().isEncryptionKey();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index bec07ce21..595f37872 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -278,13 +278,12 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
* Certify the given pubkeyid with the given masterkeyid.
*
* @param publicKeyRing Keyring to add certification to.
- * @param userIds User IDs to certify, must not be null or empty
+ * @param userIds User IDs to certify, or all if null
* @return A keyring with added certifications
*/
public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds,
byte[] nfcSignedHash, Date nfcCreationTimestamp)
- throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
- PGPException, SignatureException {
+ throws PGPException {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
@@ -314,7 +313,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
// fetch public key ring, add the certification and return it
- for (String userId : new IterableIterator<String>(userIds.iterator())) {
+ Iterable<String> it = userIds != null ? userIds
+ : new IterableIterator<String>(publicKey.getUserIDs());
+ for (String userId : it) {
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
index 3d41c928b..bd7606194 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
@@ -103,9 +103,9 @@ public class OpenPgpSignatureResultBuilder {
Log.d(Constants.TAG, "signingRing.getUnorderedUserIds(): " + signingRing.getUnorderedUserIds());
setUserIds(signingRing.getUnorderedUserIds());
- // from KEY
- setKeyExpired(signingKey.isExpired());
- setKeyRevoked(signingKey.isRevoked());
+ // either master key is expired/revoked or this specific subkey is expired/revoked
+ setKeyExpired(signingRing.isExpired() || signingKey.isExpired());
+ setKeyRevoked(signingRing.isRevoked() || signingKey.isRevoked());
}
public OpenPgpSignatureResult build() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java
new file mode 100644
index 000000000..b0c801a93
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java
@@ -0,0 +1,149 @@
+package org.sufficientlysecure.keychain.pgp;
+
+import org.spongycastle.openpgp.PGPException;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
+import org.sufficientlysecure.keychain.service.results.CertifyResult;
+import org.sufficientlysecure.keychain.service.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.service.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.service.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class PgpCertifyOperation {
+
+ private AtomicBoolean mCancelled;
+
+ private ProviderHelper mProviderHelper;
+
+ public PgpCertifyOperation(ProviderHelper providerHelper, AtomicBoolean cancelled) {
+ mProviderHelper = providerHelper;
+
+ mCancelled = cancelled;
+ }
+
+ private boolean checkCancelled() {
+ return mCancelled != null && mCancelled.get();
+ }
+
+ public CertifyResult certify(CertifyActionsParcel parcel, String passphrase) {
+
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_CRT, 0);
+
+ // Retrieve and unlock secret key
+ CanonicalizedSecretKey certificationKey;
+ try {
+ log.add(LogType.MSG_CRT_MASTER_FETCH, 1);
+ CanonicalizedSecretKeyRing secretKeyRing =
+ mProviderHelper.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId);
+ log.add(LogType.MSG_CRT_UNLOCK, 1);
+ certificationKey = secretKeyRing.getSecretKey();
+ if (!certificationKey.unlock(passphrase)) {
+ log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);
+ return new CertifyResult(CertifyResult.RESULT_ERROR, log);
+ }
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);
+ return new CertifyResult(CertifyResult.RESULT_ERROR, log);
+ } catch (NotFoundException e) {
+ log.add(LogType.MSG_CRT_ERROR_MASTER_NOT_FOUND, 2);
+ return new CertifyResult(CertifyResult.RESULT_ERROR, log);
+ }
+
+ ArrayList<UncachedKeyRing> certifiedKeys = new ArrayList<UncachedKeyRing>();
+
+ log.add(LogType.MSG_CRT_CERTIFYING, 1);
+
+ int certifyOk = 0, certifyError = 0;
+
+ // Work through all requested certifications
+ for (CertifyAction action : parcel.mCertifyActions) {
+
+ // Check if we were cancelled
+ if (checkCancelled()) {
+ log.add(LogType.MSG_OPERATION_CANCELLED, 0);
+ return new CertifyResult(CertifyResult.RESULT_CANCELLED, log);
+ }
+
+ try {
+
+ if (action.mUserIds == null) {
+ log.add(LogType.MSG_CRT_CERTIFY_ALL, 2,
+ KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
+ } else {
+ log.add(LogType.MSG_CRT_CERTIFY_SOME, 2, action.mUserIds.size(),
+ KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId));
+ }
+
+ CanonicalizedPublicKeyRing publicRing =
+ mProviderHelper.getCanonicalizedPublicKeyRing(action.mMasterKeyId);
+
+ UncachedKeyRing certifiedKey = certificationKey.certifyUserIds(publicRing, action.mUserIds, null, null);
+ certifiedKeys.add(certifiedKey);
+
+ } catch (NotFoundException e) {
+ certifyError += 1;
+ log.add(LogType.MSG_CRT_WARN_NOT_FOUND, 3);
+ } catch (PGPException e) {
+ certifyError += 1;
+ log.add(LogType.MSG_CRT_WARN_CERT_FAILED, 3);
+ Log.e(Constants.TAG, "Encountered PGPException during certification", e);
+ }
+
+ }
+
+ log.add(LogType.MSG_CRT_SAVING, 1);
+
+ // Check if we were cancelled
+ if (checkCancelled()) {
+ log.add(LogType.MSG_OPERATION_CANCELLED, 0);
+ return new CertifyResult(CertifyResult.RESULT_CANCELLED, log);
+ }
+
+ // Write all certified keys into the database
+ for (UncachedKeyRing certifiedKey : certifiedKeys) {
+
+ // Check if we were cancelled
+ if (checkCancelled()) {
+ log.add(LogType.MSG_OPERATION_CANCELLED, 0);
+ return new CertifyResult(CertifyResult.RESULT_CANCELLED, log, certifyOk, certifyError);
+ }
+
+ log.add(LogType.MSG_CRT_SAVE, 2,
+ KeyFormattingUtils.convertKeyIdToHex(certifiedKey.getMasterKeyId()));
+ // store the signed key in our local cache
+ mProviderHelper.clearLog();
+ SaveKeyringResult result = mProviderHelper.savePublicKeyRing(certifiedKey);
+
+ if (result.success()) {
+ certifyOk += 1;
+ } else {
+ log.add(LogType.MSG_CRT_WARN_SAVE_FAILED, 3);
+ }
+
+ log.add(result, 2);
+
+ // TODO do something with import results
+
+ }
+
+ if (certifyOk == 0) {
+ log.add(LogType.MSG_CRT_ERROR_NOTHING, 0);
+ return new CertifyResult(CertifyResult.RESULT_ERROR, log, certifyOk, certifyError);
+ }
+
+ log.add(LogType.MSG_CRT_SUCCESS, 0);
+ return new CertifyResult(CertifyResult.RESULT_OK, log, certifyOk, certifyError);
+
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index dd0549adc..9b21b49ce 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -38,7 +38,6 @@ import org.sufficientlysecure.keychain.service.results.OperationResult.Operation
import org.sufficientlysecure.keychain.service.results.ImportKeyResult;
import org.sufficientlysecure.keychain.service.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -118,13 +117,17 @@ public class PgpImportExport {
public ImportKeyResult importKeyRings(Iterator<ParcelableKeyRing> entries, int num) {
updateProgress(R.string.progress_importing, 0, 100);
+ OperationLog log = new OperationLog();
+
// If there aren't even any keys, do nothing here.
if (entries == null || !entries.hasNext()) {
return new ImportKeyResult(
- ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0, 0);
+ ImportKeyResult.RESULT_FAIL_NOTHING, log, 0, 0, 0, 0,
+ new long[]{});
}
int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0;
+ ArrayList<Long> importedMasterKeyIds = new ArrayList<Long>();
int position = 0;
double progSteps = 100.0 / num;
@@ -155,6 +158,7 @@ public class PgpImportExport {
}
SaveKeyringResult result;
+ mProviderHelper.clearLog();
if (key.isSecret()) {
result = mProviderHelper.saveSecretKeyRing(key,
new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
@@ -166,13 +170,17 @@ public class PgpImportExport {
badKeys += 1;
} else if (result.updated()) {
oldKeys += 1;
+ importedMasterKeyIds.add(key.getMasterKeyId());
} else {
newKeys += 1;
if (key.isSecret()) {
secret += 1;
}
+ importedMasterKeyIds.add(key.getMasterKeyId());
}
+ log.add(result, 1);
+
} catch (IOException e) {
Log.e(Constants.TAG, "Encountered bad key on import!", e);
++badKeys;
@@ -184,7 +192,6 @@ public class PgpImportExport {
position++;
}
- OperationLog log = mProviderHelper.getLog();
int resultType = 0;
// special return case: no new keys at all
if (badKeys == 0 && newKeys == 0 && oldKeys == 0) {
@@ -211,8 +218,14 @@ public class PgpImportExport {
resultType |= ImportKeyResult.RESULT_CANCELLED;
}
- return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret);
+ // convert to long array
+ long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()];
+ for (int i = 0; i < importedMasterKeyIds.size(); ++i) {
+ importedMasterKeyIdsArray[i] = importedMasterKeyIds.get(i);
+ }
+ return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret,
+ importedMasterKeyIdsArray);
}
public Bundle exportKeyRings(ArrayList<Long> publicKeyRingMasterIds,
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 c4070e39a..43256fe28 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -557,7 +557,7 @@ public class PgpKeyOperation {
// keep track if we actually changed one
boolean ok = false;
- log.add(LogType.MSG_MF_UID_PRIMARY, indent);
+ log.add(LogType.MSG_MF_UID_PRIMARY, indent, saveParcel.mChangePrimaryUserId);
indent += 1;
// we work on the modifiedPublicKey here, to respect new or newly revoked uids
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 7c640efb8..17d35dc1f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -366,6 +366,11 @@ public class UncachedKeyRing {
for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) {
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
+ // warn if user id was made with bad encoding
+ if (!Utf8Util.isValidUTF8(rawUserId)) {
+ log.add(LogType.MSG_KC_UID_WARN_ENCODING, indent);
+ }
+
// check for duplicate user ids
if (processedUserIds.contains(userId)) {
log.add(LogType.MSG_KC_UID_DUP,
@@ -437,10 +442,6 @@ public class UncachedKeyRing {
badCerts += 1;
continue;
}
- // warn user if the signature was made with bad encoding
- if (!Utf8Util.isValidUTF8(rawUserId)) {
- log.add(LogType.MSG_KC_UID_WARN_ENCODING, indent);
- }
} catch (PgpGeneralException e) {
log.add(LogType.MSG_KC_UID_BAD_ERR,
indent, userId);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
index ad3ebae5f..5a3770f2d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
@@ -125,9 +125,9 @@ public class CachedPublicKeyRing extends KeyRing {
public boolean canCertify() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.CAN_CERTIFY,
- ProviderHelper.FIELD_TYPE_INTEGER);
- return (Long) data > 0;
+ KeychainContract.KeyRings.HAS_CERTIFY,
+ ProviderHelper.FIELD_TYPE_NULL);
+ return !((Boolean) data);
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 33f51cbf9..6127002bb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -89,7 +89,6 @@ public class KeychainContract {
.parse("content://" + CONTENT_AUTHORITY);
public static final String BASE_KEY_RINGS = "key_rings";
- public static final String BASE_DATA = "data";
public static final String PATH_UNIFIED = "unified";
@@ -243,6 +242,10 @@ public class KeychainContract {
public static final String CONTENT_ITEM_TYPE
= "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.user_ids";
+ public static Uri buildUserIdsUri() {
+ return CONTENT_URI.buildUpon().appendPath(PATH_USER_IDS).build();
+ }
+
public static Uri buildUserIdsUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_USER_IDS).build();
}
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 80f4610a4..d40287690 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -51,6 +51,7 @@ public class KeychainProvider extends ContentProvider {
private static final int KEY_RINGS_UNIFIED = 101;
private static final int KEY_RINGS_PUBLIC = 102;
private static final int KEY_RINGS_SECRET = 103;
+ private static final int KEY_RINGS_USER_IDS = 104;
private static final int KEY_RING_UNIFIED = 200;
private static final int KEY_RING_KEYS = 201;
@@ -85,17 +86,22 @@ public class KeychainProvider extends ContentProvider {
* <pre>
* key_rings/unified
* key_rings/public
+ * key_rings/secret
+ * key_rings/user_ids
* </pre>
*/
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
- + "/" + KeychainContract.PATH_UNIFIED,
+ + "/" + KeychainContract.PATH_UNIFIED,
KEY_RINGS_UNIFIED);
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
- + "/" + KeychainContract.PATH_PUBLIC,
+ + "/" + KeychainContract.PATH_PUBLIC,
KEY_RINGS_PUBLIC);
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
- + "/" + KeychainContract.PATH_SECRET,
+ + "/" + KeychainContract.PATH_SECRET,
KEY_RINGS_SECRET);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ + "/" + KeychainContract.PATH_USER_IDS,
+ KEY_RINGS_USER_IDS);
/**
* find by criteria other than master key id
@@ -450,6 +456,7 @@ public class KeychainProvider extends ContentProvider {
break;
}
+ case KEY_RINGS_USER_IDS:
case KEY_RING_USER_IDS: {
HashMap<String, String> projectionMap = new HashMap<String, String>();
projectionMap.put(UserIds._ID, Tables.USER_IDS + ".oid AS _id");
@@ -470,13 +477,18 @@ public class KeychainProvider extends ContentProvider {
+ Tables.CERTS + "." + Certs.RANK
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0"
+ ")");
- groupBy = Tables.USER_IDS + "." + UserIds.RANK;
+ groupBy = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
+ + ", " + Tables.USER_IDS + "." + UserIds.RANK;
- qb.appendWhere(Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+ // If we are searching for a particular keyring's ids, add where
+ if (match == KEY_RING_USER_IDS) {
+ qb.appendWhere(Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+ }
if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIds.RANK + " ASC";
+ sortOrder = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " ASC"
+ + "," + Tables.USER_IDS + "." + UserIds.RANK + " ASC";
}
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 6b96a1e6e..273f9c75f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -133,6 +133,12 @@ public class ProviderHelper {
}
}
+ public void clearLog() {
+ if (mLog != null) {
+ mLog.clear();
+ }
+ }
+
// If we ever switch to api level 11, we can ditch this whole mess!
public static final int FIELD_TYPE_NULL = 1;
// this is called integer to stay coherent with the constants in Cursor (api level 11)
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
new file mode 100644
index 000000000..dd9c0d769
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * This class is a a transferable representation for a number of keyrings to
+ * be certified.
+ */
+public class CertifyActionsParcel implements Parcelable {
+
+ // the master key id to certify with
+ final public long mMasterKeyId;
+ public CertifyLevel mLevel;
+
+ public ArrayList<CertifyAction> mCertifyActions = new ArrayList<CertifyAction>();
+
+ public CertifyActionsParcel(long masterKeyId) {
+ mMasterKeyId = masterKeyId;
+ mLevel = CertifyLevel.DEFAULT;
+ }
+
+ public CertifyActionsParcel(Parcel source) {
+ mMasterKeyId = source.readLong();
+ // just like parcelables, this is meant for ad-hoc IPC only and is NOT portable!
+ mLevel = CertifyLevel.values()[source.readInt()];
+
+ mCertifyActions = (ArrayList<CertifyAction>) source.readSerializable();
+ }
+
+ public void add(CertifyAction action) {
+ mCertifyActions.add(action);
+ }
+
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeLong(mMasterKeyId);
+ destination.writeInt(mLevel.ordinal());
+
+ destination.writeSerializable(mCertifyActions);
+ }
+
+ public static final Creator<CertifyActionsParcel> CREATOR = new Creator<CertifyActionsParcel>() {
+ public CertifyActionsParcel createFromParcel(final Parcel source) {
+ return new CertifyActionsParcel(source);
+ }
+
+ public CertifyActionsParcel[] newArray(final int size) {
+ return new CertifyActionsParcel[size];
+ }
+ };
+
+ // TODO make this parcelable
+ public static class CertifyAction implements Serializable {
+ final public long mMasterKeyId;
+
+ final public ArrayList<String> mUserIds;
+
+ public CertifyAction(long masterKeyId) {
+ this(masterKeyId, null);
+ }
+
+ public CertifyAction(long masterKeyId, ArrayList<String> userIds) {
+ mMasterKeyId = masterKeyId;
+ mUserIds = userIds;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ String out = "mMasterKeyId: " + mMasterKeyId + "\n";
+ out += "mLevel: " + mLevel + "\n";
+ out += "mCertifyActions: " + mCertifyActions + "\n";
+
+ return out;
+ }
+
+ // All supported algorithms
+ public enum CertifyLevel {
+ DEFAULT, NONE, CASUAL, POSITIVE
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index 2101705bc..b9c42db3f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -29,7 +29,9 @@ import android.os.RemoteException;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
+import org.sufficientlysecure.keychain.service.results.CertifyResult;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -39,7 +41,6 @@ import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
@@ -80,7 +81,6 @@ import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -183,10 +183,8 @@ public class KeychainIntentService extends IntentService implements Progressable
public static final String DOWNLOAD_KEY_SERVER = "query_key_server";
public static final String DOWNLOAD_KEY_LIST = "query_key_id";
- // sign key
- public static final String CERTIFY_KEY_MASTER_KEY_ID = "sign_key_master_key_id";
- public static final String CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
- public static final String CERTIFY_KEY_UIDS = "sign_key_uids";
+ // certify key
+ public static final String CERTIFY_PARCEL = "certify_parcel";
// consolidate
public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
@@ -253,90 +251,75 @@ public class KeychainIntentService extends IntentService implements Progressable
String action = intent.getAction();
// executeServiceMethod action from extra bundle
- if (ACTION_SIGN_ENCRYPT.equals(action)) {
- try {
- /* Input */
- int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
- Bundle resultData = new Bundle();
-
- long sigMasterKeyId = data.getLong(ENCRYPT_SIGNATURE_MASTER_ID);
- String sigKeyPassphrase = data.getString(ENCRYPT_SIGNATURE_KEY_PASSPHRASE);
+ if (ACTION_CERTIFY_KEYRING.equals(action)) {
- byte[] nfcHash = data.getByteArray(ENCRYPT_SIGNATURE_NFC_HASH);
- Date nfcTimestamp = (Date) data.getSerializable(ENCRYPT_SIGNATURE_NFC_TIMESTAMP);
+ try {
- String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE);
+ /* Input */
+ CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL);
- boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_ARMOR);
- long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS);
- int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID);
- int urisCount = data.containsKey(ENCRYPT_INPUT_URIS) ? data.getParcelableArrayList(ENCRYPT_INPUT_URIS).size() : 1;
- for (int i = 0; i < urisCount; i++) {
- data.putInt(SELECTED_URI, i);
- InputData inputData = createEncryptInputData(data);
- OutputStream outStream = createCryptOutputStream(data);
- String originalFilename = getOriginalFilename(data);
+ /* Operation */
+ String passphrase = PassphraseCacheService.getCachedPassphrase(this,
+ // certification is always with the master key id, so use that one
+ parcel.mMasterKeyId, parcel.mMasterKeyId);
+ if (passphrase == null) {
+ throw new PgpGeneralException("Unable to obtain passphrase");
+ }
- /* Operation */
- PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
- new ProviderHelper(this), this, inputData, outStream
- );
- builder.setProgressable(this)
- .setEnableAsciiArmorOutput(useAsciiArmor)
- .setVersionHeader(PgpHelper.getVersionForHeader(this))
- .setCompressionId(compressionId)
- .setSymmetricEncryptionAlgorithm(
- Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
- .setEncryptionMasterKeyIds(encryptionKeyIds)
- .setSymmetricPassphrase(symmetricPassphrase)
- .setOriginalFilename(originalFilename);
+ ProviderHelper providerHelper = new ProviderHelper(this);
+ PgpCertifyOperation op = new PgpCertifyOperation(providerHelper, mActionCanceled);
+ CertifyResult result = op.certify(parcel, passphrase);
- try {
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- // Find the appropriate subkey to sign with
- CachedPublicKeyRing signingRing =
- new ProviderHelper(this).getCachedPublicKeyRing(sigMasterKeyId);
- long sigSubKeyId = signingRing.getSecretSignId();
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
- // Set signature settings
- builder.setSignatureMasterKeyId(sigMasterKeyId)
- .setSignatureSubKeyId(sigSubKeyId)
- .setSignaturePassphrase(sigKeyPassphrase)
- .setSignatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .setAdditionalEncryptId(sigMasterKeyId);
- if (nfcHash != null && nfcTimestamp != null) {
- builder.setNfcState(nfcHash, nfcTimestamp);
- }
+ } else if (ACTION_CONSOLIDATE.equals(action)) {
- } catch (PgpGeneralException e) {
- // encrypt-only
- // TODO Just silently drop the requested signature? Shouldn't we throw here?
- }
+ ConsolidateResult result;
+ if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
+ result = new ProviderHelper(this).consolidateDatabaseStep2(this);
+ } else {
+ result = new ProviderHelper(this).consolidateDatabaseStep1(this);
+ }
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- // this assumes that the bytes are cleartext (valid for current implementation!)
- if (source == IO_BYTES) {
- builder.setCleartextInput(true);
- }
+ } else if (ACTION_DECRYPT_METADATA.equals(action)) {
- SignEncryptResult result = builder.build().execute();
- resultData.putParcelable(SignEncryptResult.EXTRA_RESULT, result);
+ try {
+ /* Input */
+ String passphrase = data.getString(DECRYPT_PASSPHRASE);
+ byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
- outStream.close();
+ InputData inputData = createDecryptInputData(data);
- /* Output */
+ /* Operation */
- finalizeEncryptOutputStream(data, resultData, outStream);
+ Bundle resultData = new Bundle();
- }
+ // verifyText and decrypt returning additional resultData values for the
+ // verification of signatures
+ PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
+ new ProviderHelper(this),
+ this, inputData, null
+ );
+ builder.setProgressable(this)
+ .setAllowSymmetricDecryption(true)
+ .setPassphrase(passphrase)
+ .setDecryptMetadataOnly(true)
+ .setNfcState(nfcDecryptedSessionKey);
- Log.logDebugBundle(resultData, "resultData");
+ DecryptVerifyResult decryptVerifyResult = builder.build().execute();
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, decryptVerifyResult);
} catch (Exception e) {
sendErrorToHandler(e);
}
+
} else if (ACTION_DECRYPT_VERIFY.equals(action)) {
+
try {
/* Input */
String passphrase = data.getString(DECRYPT_PASSPHRASE);
@@ -376,42 +359,128 @@ public class KeychainIntentService extends IntentService implements Progressable
} catch (Exception e) {
sendErrorToHandler(e);
}
- } else if (ACTION_DECRYPT_METADATA.equals(action)) {
+
+ } else if (ACTION_DELETE.equals(action)) {
+
try {
- /* Input */
- String passphrase = data.getString(DECRYPT_PASSPHRASE);
- byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
- InputData inputData = createDecryptInputData(data);
+ long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
+ boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
- /* Operation */
+ if (masterKeyIds.length == 0) {
+ throw new PgpGeneralException("List of keys to delete is empty");
+ }
- Bundle resultData = new Bundle();
+ if (isSecret && masterKeyIds.length > 1) {
+ throw new PgpGeneralException("Secret keys can only be deleted individually!");
+ }
- // verifyText and decrypt returning additional resultData values for the
- // verification of signatures
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- new ProviderHelper(this),
- this, inputData, null
- );
- builder.setProgressable(this)
- .setAllowSymmetricDecryption(true)
- .setPassphrase(passphrase)
- .setDecryptMetadataOnly(true)
- .setNfcState(nfcDecryptedSessionKey);
+ boolean success = false;
+ for (long masterKeyId : masterKeyIds) {
+ int count = getContentResolver().delete(
+ KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null
+ );
+ success |= count > 0;
+ }
- DecryptVerifyResult decryptVerifyResult = builder.build().execute();
+ if (isSecret && success) {
+ new ProviderHelper(this).consolidateDatabaseStep1(this);
+ }
- resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult);
+ if (success) {
+ // make sure new data is synced into contacts
+ ContactSyncAdapterService.requestSync();
- /* Output */
- Log.logDebugBundle(resultData, "resultData");
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+ }
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
+ } else if (ACTION_DELETE_FILE_SECURELY.equals(action)) {
+
+ try {
+ /* Input */
+ String deleteFile = data.getString(DELETE_FILE);
+
+ /* Operation */
+ try {
+ PgpHelper.deleteFileSecurely(this, this, new File(deleteFile));
+ } catch (FileNotFoundException e) {
+ throw new PgpGeneralException(
+ getString(R.string.error_file_not_found, deleteFile));
+ } catch (IOException e) {
+ throw new PgpGeneralException(getString(R.string.error_file_delete_failed,
+ deleteFile));
+ }
+
+ /* Output */
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
} catch (Exception e) {
sendErrorToHandler(e);
}
+
+ } else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action) || ACTION_IMPORT_KEYBASE_KEYS.equals(action)) {
+
+ ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
+
+ // this downloads the keys and places them into the ImportKeysListEntry entries
+ String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
+
+ ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
+ for (ImportKeysListEntry entry : entries) {
+ try {
+ Keyserver server;
+ ArrayList<String> origins = entry.getOrigins();
+ if (origins == null) {
+ origins = new ArrayList<String>();
+ }
+ if (origins.isEmpty()) {
+ origins.add(keyServer);
+ }
+ for (String origin : origins) {
+ if (KeybaseKeyserver.ORIGIN.equals(origin)) {
+ server = new KeybaseKeyserver();
+ } else {
+ server = new HkpKeyserver(origin);
+ }
+ Log.d(Constants.TAG, "IMPORTING " + entry.getKeyIdHex() + " FROM: " + server);
+
+ // if available use complete fingerprint for get request
+ byte[] downloadedKeyBytes;
+ if (KeybaseKeyserver.ORIGIN.equals(origin)) {
+ downloadedKeyBytes = server.get(entry.getExtraData()).getBytes();
+ } else if (entry.getFingerprintHex() != null) {
+ downloadedKeyBytes = server.get("0x" + entry.getFingerprintHex()).getBytes();
+ } else {
+ downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
+ }
+
+ // save key bytes in entry object for doing the
+ // actual import afterwards
+ keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex()));
+ }
+ } catch (Keyserver.QueryFailedException e) {
+ sendErrorToHandler(e);
+ }
+ }
+
+ Intent importIntent = new Intent(this, KeychainIntentService.class);
+ importIntent.setAction(ACTION_IMPORT_KEYRING);
+
+ Bundle importData = new Bundle();
+ // This is not going through binder, nothing to fear of
+ importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
+ importIntent.putExtra(EXTRA_DATA, importData);
+ importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
+
+ // now import it with this service
+ onHandleIntent(importIntent);
+
+ // result is handled in ACTION_IMPORT_KEYRING
+
} else if (ACTION_EDIT_KEYRING.equals(action)) {
+
try {
/* Input */
SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
@@ -438,11 +507,7 @@ public class KeychainIntentService extends IntentService implements Progressable
// If the edit operation didn't succeed, exit here
if (!modifyResult.success()) {
// always return SaveKeyringResult, so create one out of the EditKeyResult
- SaveKeyringResult saveResult = new SaveKeyringResult(
- SaveKeyringResult.RESULT_ERROR,
- modifyResult.getLog(),
- null);
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, modifyResult);
return;
}
@@ -456,9 +521,9 @@ public class KeychainIntentService extends IntentService implements Progressable
log.add(LogType.MSG_OPERATION_CANCELLED, 0);
}
// If so, just stop without saving
- SaveKeyringResult saveResult = new SaveKeyringResult(
- SaveKeyringResult.RESULT_CANCELLED, log, null);
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult);
+ modifyResult = new EditKeyResult(
+ EditKeyResult.RESULT_CANCELLED, log, null);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, modifyResult);
return;
}
@@ -489,66 +554,8 @@ public class KeychainIntentService extends IntentService implements Progressable
sendErrorToHandler(e);
}
- } else if (ACTION_DELETE_FILE_SECURELY.equals(action)) {
- try {
- /* Input */
- String deleteFile = data.getString(DELETE_FILE);
-
- /* Operation */
- try {
- PgpHelper.deleteFileSecurely(this, this, new File(deleteFile));
- } catch (FileNotFoundException e) {
- throw new PgpGeneralException(
- getString(R.string.error_file_not_found, deleteFile));
- } catch (IOException e) {
- throw new PgpGeneralException(getString(R.string.error_file_delete_failed,
- deleteFile));
- }
-
- /* Output */
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
- } else if (ACTION_IMPORT_KEYRING.equals(action)) {
- try {
-
- Iterator<ParcelableKeyRing> entries;
- int numEntries;
- if (data.containsKey(IMPORT_KEY_LIST)) {
- // get entries from intent
- ArrayList<ParcelableKeyRing> list = data.getParcelableArrayList(IMPORT_KEY_LIST);
- entries = list.iterator();
- numEntries = list.size();
- } else {
- // get entries from cached file
- ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl");
- IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
- entries = it;
- numEntries = it.getSize();
- }
-
- ProviderHelper providerHelper = new ProviderHelper(this);
- PgpImportExport pgpImportExport = new PgpImportExport(
- this, providerHelper, this, mActionCanceled);
- ImportKeyResult result = pgpImportExport.importKeyRings(entries, numEntries);
-
- // we do this even on failure or cancellation!
- if (result.mSecret > 0) {
- // cannot cancel from here on out!
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_PREVENT_CANCEL);
- providerHelper.consolidateDatabaseStep1(this);
- }
-
- // make sure new data is synced into contacts
- ContactSyncAdapterService.requestSync();
-
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
} else if (ACTION_EXPORT_KEYRING.equals(action)) {
+
try {
boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
@@ -614,166 +621,157 @@ public class KeychainIntentService extends IntentService implements Progressable
} catch (Exception e) {
sendErrorToHandler(e);
}
- } else if (ACTION_UPLOAD_KEYRING.equals(action)) {
- try {
- /* Input */
- String keyServer = data.getString(UPLOAD_KEY_SERVER);
- // and dataUri!
+ } else if (ACTION_IMPORT_KEYRING.equals(action)) {
- /* Operation */
- HkpKeyserver server = new HkpKeyserver(keyServer);
+ try {
+
+ Iterator<ParcelableKeyRing> entries;
+ int numEntries;
+ if (data.containsKey(IMPORT_KEY_LIST)) {
+ // get entries from intent
+ ArrayList<ParcelableKeyRing> list = data.getParcelableArrayList(IMPORT_KEY_LIST);
+ entries = list.iterator();
+ numEntries = list.size();
+ } else {
+ // get entries from cached file
+ ParcelableFileCache<ParcelableKeyRing> cache =
+ new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl");
+ IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
+ entries = it;
+ numEntries = it.getSize();
+ }
ProviderHelper providerHelper = new ProviderHelper(this);
- CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
- PgpImportExport pgpImportExport = new PgpImportExport(this, new ProviderHelper(this), this);
+ PgpImportExport pgpImportExport = new PgpImportExport(
+ this, providerHelper, this, mActionCanceled);
+ ImportKeyResult result = pgpImportExport.importKeyRings(entries, numEntries);
- try {
- pgpImportExport.uploadKeyRingToServer(server, keyring);
- } catch (Keyserver.AddKeyException e) {
- throw new PgpGeneralException("Unable to export key to selected server");
+ // we do this even on failure or cancellation!
+ if (result.mSecret > 0) {
+ // cannot cancel from here on out!
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_PREVENT_CANCEL);
+ providerHelper.consolidateDatabaseStep1(this);
}
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+ // make sure new data is synced into contacts
+ ContactSyncAdapterService.requestSync();
+
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
} catch (Exception e) {
sendErrorToHandler(e);
}
- } else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action) || ACTION_IMPORT_KEYBASE_KEYS.equals(action)) {
- ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
- // this downloads the keys and places them into the ImportKeysListEntry entries
- String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
+ } else if (ACTION_SIGN_ENCRYPT.equals(action)) {
+
+ try {
+ /* Input */
+ int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
+ Bundle resultData = new Bundle();
+
+ long sigMasterKeyId = data.getLong(ENCRYPT_SIGNATURE_MASTER_ID);
+ String sigKeyPassphrase = data.getString(ENCRYPT_SIGNATURE_KEY_PASSPHRASE);
+
+ byte[] nfcHash = data.getByteArray(ENCRYPT_SIGNATURE_NFC_HASH);
+ Date nfcTimestamp = (Date) data.getSerializable(ENCRYPT_SIGNATURE_NFC_TIMESTAMP);
+
+ String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE);
+
+ boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_ARMOR);
+ long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS);
+ int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID);
+ int urisCount = data.containsKey(ENCRYPT_INPUT_URIS) ? data.getParcelableArrayList(ENCRYPT_INPUT_URIS).size() : 1;
+ for (int i = 0; i < urisCount; i++) {
+ data.putInt(SELECTED_URI, i);
+ InputData inputData = createEncryptInputData(data);
+ OutputStream outStream = createCryptOutputStream(data);
+ String originalFilename = getOriginalFilename(data);
+
+ /* Operation */
+ PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
+ new ProviderHelper(this), this, inputData, outStream
+ );
+ builder.setProgressable(this)
+ .setEnableAsciiArmorOutput(useAsciiArmor)
+ .setVersionHeader(PgpHelper.getVersionForHeader(this))
+ .setCompressionId(compressionId)
+ .setSymmetricEncryptionAlgorithm(
+ Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
+ .setEncryptionMasterKeyIds(encryptionKeyIds)
+ .setSymmetricPassphrase(symmetricPassphrase)
+ .setOriginalFilename(originalFilename);
- ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
- for (ImportKeysListEntry entry : entries) {
try {
- Keyserver server;
- ArrayList<String> origins = entry.getOrigins();
- if (origins == null) {
- origins = new ArrayList<String>();
- }
- if (origins.isEmpty()) {
- origins.add(keyServer);
- }
- for (String origin : origins) {
- if (KeybaseKeyserver.ORIGIN.equals(origin)) {
- server = new KeybaseKeyserver();
- } else {
- server = new HkpKeyserver(origin);
- }
- Log.d(Constants.TAG, "IMPORTING " + entry.getKeyIdHex() + " FROM: " + server);
-
- // if available use complete fingerprint for get request
- byte[] downloadedKeyBytes;
- if (KeybaseKeyserver.ORIGIN.equals(origin)) {
- downloadedKeyBytes = server.get(entry.getExtraData()).getBytes();
- } else if (entry.getFingerprintHex() != null) {
- downloadedKeyBytes = server.get("0x" + entry.getFingerprintHex()).getBytes();
- } else {
- downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
- }
-
- // save key bytes in entry object for doing the
- // actual import afterwards
- keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex()));
+
+ // Find the appropriate subkey to sign with
+ CachedPublicKeyRing signingRing =
+ new ProviderHelper(this).getCachedPublicKeyRing(sigMasterKeyId);
+ long sigSubKeyId = signingRing.getSecretSignId();
+
+ // Set signature settings
+ builder.setSignatureMasterKeyId(sigMasterKeyId)
+ .setSignatureSubKeyId(sigSubKeyId)
+ .setSignaturePassphrase(sigKeyPassphrase)
+ .setSignatureHashAlgorithm(
+ Preferences.getPreferences(this).getDefaultHashAlgorithm())
+ .setAdditionalEncryptId(sigMasterKeyId);
+ if (nfcHash != null && nfcTimestamp != null) {
+ builder.setNfcState(nfcHash, nfcTimestamp);
}
- } catch (Exception e) {
- sendErrorToHandler(e);
+
+ } catch (PgpGeneralException e) {
+ // encrypt-only
+ // TODO Just silently drop the requested signature? Shouldn't we throw here?
}
- }
- Intent importIntent = new Intent(this, KeychainIntentService.class);
- importIntent.setAction(ACTION_IMPORT_KEYRING);
+ // this assumes that the bytes are cleartext (valid for current implementation!)
+ if (source == IO_BYTES) {
+ builder.setCleartextInput(true);
+ }
- Bundle importData = new Bundle();
- // This is not going through binder, nothing to fear of
- importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
- importIntent.putExtra(EXTRA_DATA, importData);
- importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
+ SignEncryptResult result = builder.build().execute();
+ resultData.putParcelable(SignEncryptResult.EXTRA_RESULT, result);
- // now import it with this service
- onHandleIntent(importIntent);
+ outStream.close();
- // result is handled in ACTION_IMPORT_KEYRING
- } else if (ACTION_CERTIFY_KEYRING.equals(action)) {
- try {
+ /* Output */
- /* Input */
- long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID);
- long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID);
- ArrayList<String> userIds = data.getStringArrayList(CERTIFY_KEY_UIDS);
+ finalizeEncryptOutputStream(data, resultData, outStream);
- /* Operation */
- String signaturePassphrase = PassphraseCacheService.getCachedPassphrase(this,
- masterKeyId, masterKeyId);
- if (signaturePassphrase == null) {
- throw new PgpGeneralException("Unable to obtain passphrase");
}
- ProviderHelper providerHelper = new ProviderHelper(this);
- CanonicalizedPublicKeyRing publicRing = providerHelper.getCanonicalizedPublicKeyRing(pubKeyId);
- CanonicalizedSecretKeyRing secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(masterKeyId);
- CanonicalizedSecretKey certificationKey = secretKeyRing.getSecretKey();
- if (!certificationKey.unlock(signaturePassphrase)) {
- throw new PgpGeneralException("Error extracting key (bad passphrase?)");
- }
- // TODO: supply nfc stuff
- UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds, null, null);
-
- // store the signed key in our local cache
- providerHelper.savePublicKeyRing(newRing);
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+ Log.logDebugBundle(resultData, "resultData");
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {
sendErrorToHandler(e);
}
- } else if (ACTION_DELETE.equals(action)) {
+ } else if (ACTION_UPLOAD_KEYRING.equals(action)) {
try {
- long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
- boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
-
- if (masterKeyIds.length == 0) {
- throw new PgpGeneralException("List of keys to delete is empty");
- }
-
- if (isSecret && masterKeyIds.length > 1) {
- throw new PgpGeneralException("Secret keys can only be deleted individually!");
- }
-
- boolean success = false;
- for (long masterKeyId : masterKeyIds) {
- int count = getContentResolver().delete(
- KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null
- );
- success |= count > 0;
- }
+ /* Input */
+ String keyServer = data.getString(UPLOAD_KEY_SERVER);
+ // and dataUri!
- if (isSecret && success) {
- new ProviderHelper(this).consolidateDatabaseStep1(this);
- }
+ /* Operation */
+ HkpKeyserver server = new HkpKeyserver(keyServer);
- if (success) {
- // make sure new data is synced into contacts
- ContactSyncAdapterService.requestSync();
+ ProviderHelper providerHelper = new ProviderHelper(this);
+ CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
+ PgpImportExport pgpImportExport = new PgpImportExport(this, new ProviderHelper(this), this);
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+ try {
+ pgpImportExport.uploadKeyRingToServer(server, keyring);
+ } catch (Keyserver.AddKeyException e) {
+ throw new PgpGeneralException("Unable to export key to selected server");
}
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
} catch (Exception e) {
sendErrorToHandler(e);
}
-
- } else if (ACTION_CONSOLIDATE.equals(action)) {
- ConsolidateResult result;
- if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
- result = new ProviderHelper(this).consolidateDatabaseStep2(this);
- } else {
- result = new ProviderHelper(this).consolidateDatabaseStep1(this);
- }
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/CertifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/CertifyResult.java
new file mode 100644
index 000000000..49bc613bd
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/CertifyResult.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service.results;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.View;
+
+import com.github.johnpersano.supertoasts.SuperCardToast;
+import com.github.johnpersano.supertoasts.SuperToast;
+import com.github.johnpersano.supertoasts.SuperToast.Duration;
+import com.github.johnpersano.supertoasts.util.OnClickWrapper;
+import com.github.johnpersano.supertoasts.util.Style;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
+import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
+
+public class CertifyResult extends OperationResult {
+
+ int mCertifyOk, mCertifyError;
+
+ public CertifyResult(int result, OperationLog log) {
+ super(result, log);
+ }
+
+ public CertifyResult(int result, OperationLog log, int certifyOk, int certifyError) {
+ this(result, log);
+ mCertifyOk = certifyOk;
+ mCertifyError = certifyError;
+ }
+
+ /** Construct from a parcel - trivial because we have no extra data. */
+ public CertifyResult(Parcel source) {
+ super(source);
+ mCertifyOk = source.readInt();
+ mCertifyError = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mCertifyOk);
+ dest.writeInt(mCertifyError);
+ }
+
+ public static Creator<CertifyResult> CREATOR = new Creator<CertifyResult>() {
+ public CertifyResult createFromParcel(final Parcel source) {
+ return new CertifyResult(source);
+ }
+
+ public CertifyResult[] newArray(final int size) {
+ return new CertifyResult[size];
+ }
+ };
+
+ public SuperCardToast createNotify(final Activity activity) {
+
+ int resultType = getResult();
+
+ String str;
+ int duration, color;
+
+ // Not an overall failure
+ if ((resultType & OperationResult.RESULT_ERROR) == 0) {
+ String withWarnings;
+
+ duration = Duration.EXTRA_LONG;
+ color = Style.GREEN;
+ withWarnings = "";
+
+ // Any warnings?
+ if ((resultType & ImportKeyResult.RESULT_WARNINGS) > 0) {
+ duration = 0;
+ color = Style.ORANGE;
+ withWarnings += activity.getString(R.string.with_warnings);
+ }
+ if ((resultType & ImportKeyResult.RESULT_CANCELLED) > 0) {
+ duration = 0;
+ color = Style.ORANGE;
+ withWarnings += activity.getString(R.string.with_cancelled);
+ }
+
+ // New and updated keys
+ str = activity.getResources().getQuantityString(
+ R.plurals.certify_keys_ok, mCertifyOk, mCertifyOk, withWarnings);
+ if (mCertifyError > 0) {
+ // definitely switch to warning-style message in this case!
+ duration = 0;
+ color = Style.RED;
+ str += " " + activity.getResources().getQuantityString(
+ R.plurals.certify_keys_with_errors, mCertifyError, mCertifyError);
+ }
+
+ } else {
+ duration = 0;
+ color = Style.RED;
+ str = activity.getResources().getQuantityString(R.plurals.certify_error,
+ mCertifyError, mCertifyError);
+ }
+
+ boolean button = getLog() != null && !getLog().isEmpty();
+ SuperCardToast toast = new SuperCardToast(activity,
+ button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD,
+ Style.getStyle(color, SuperToast.Animations.POPUP));
+ toast.setText(str);
+ toast.setDuration(duration);
+ toast.setIndeterminate(duration == 0);
+ toast.setSwipeToDismiss(true);
+ // If we have a log and it's non-empty, show a View Log button
+ if (button) {
+ toast.setButtonIcon(R.drawable.ic_action_view_as_list,
+ activity.getResources().getString(R.string.view_log));
+ toast.setButtonTextColor(activity.getResources().getColor(R.color.black));
+ toast.setTextColor(activity.getResources().getColor(R.color.black));
+ toast.setOnClickWrapper(new OnClickWrapper("supercardtoast",
+ new SuperToast.OnClickListener() {
+ @Override
+ public void onClick(View view, Parcelable token) {
+ Intent intent = new Intent(
+ activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, CertifyResult.this);
+ activity.startActivity(intent);
+ }
+ }
+ ));
+ }
+
+ return toast;
+
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/GetKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/GetKeyResult.java
new file mode 100644
index 000000000..e76d1bd41
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/GetKeyResult.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service.results;
+
+import android.os.Parcel;
+
+public class GetKeyResult extends OperationResult {
+
+ public int mNonPgpPartsCount;
+
+ public int getNonPgpPartsCount() {
+ return mNonPgpPartsCount;
+ }
+
+ public void setNonPgpPartsCount(int nonPgpPartsCount) {
+ mNonPgpPartsCount = nonPgpPartsCount;
+ }
+
+ public GetKeyResult(int result, OperationLog log) {
+ super(result, log);
+ }
+
+ public static final int RESULT_ERROR_NO_VALID_KEYS = RESULT_ERROR + 8;
+ public static final int RESULT_ERROR_NO_PGP_PARTS = RESULT_ERROR + 16;
+ public static final int RESULT_ERROR_QUERY_TOO_SHORT = RESULT_ERROR + 32;
+ public static final int RESULT_ERROR_TOO_MANY_RESPONSES = RESULT_ERROR + 64;
+ public static final int RESULT_ERROR_TOO_SHORT_OR_TOO_MANY_RESPONSES = RESULT_ERROR + 128;
+ public static final int RESULT_ERROR_QUERY_FAILED = RESULT_ERROR + 256;
+
+ public GetKeyResult(Parcel source) {
+ super(source);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ }
+
+ public static Creator<GetKeyResult> CREATOR = new Creator<GetKeyResult>() {
+ public GetKeyResult createFromParcel(final Parcel source) {
+ return new GetKeyResult(source);
+ }
+
+ public GetKeyResult[] newArray(final int size) {
+ return new GetKeyResult[size];
+ }
+ };
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/ImportKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/ImportKeyResult.java
index 188162aec..b54dcc5d6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/ImportKeyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/ImportKeyResult.java
@@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
public class ImportKeyResult extends OperationResult {
public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret;
+ public final long[] mImportedMasterKeyIds;
// At least one new key
public static final int RESULT_OK_NEWKEYS = 8;
@@ -69,21 +70,28 @@ public class ImportKeyResult extends OperationResult {
return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING;
}
+ public long[] getImportedMasterKeyIds() {
+ return mImportedMasterKeyIds;
+ }
+
public ImportKeyResult(Parcel source) {
super(source);
mNewKeys = source.readInt();
mUpdatedKeys = source.readInt();
mBadKeys = source.readInt();
mSecret = source.readInt();
+ mImportedMasterKeyIds = source.createLongArray();
}
public ImportKeyResult(int result, OperationLog log,
- int newKeys, int updatedKeys, int badKeys, int secret) {
+ int newKeys, int updatedKeys, int badKeys, int secret,
+ long[] importedMasterKeyIds) {
super(result, log);
mNewKeys = newKeys;
mUpdatedKeys = updatedKeys;
mBadKeys = badKeys;
mSecret = secret;
+ mImportedMasterKeyIds = importedMasterKeyIds;
}
@Override
@@ -93,6 +101,7 @@ public class ImportKeyResult extends OperationResult {
dest.writeInt(mUpdatedKeys);
dest.writeInt(mBadKeys);
dest.writeInt(mSecret);
+ dest.writeLongArray(mImportedMasterKeyIds);
}
public static Creator<ImportKeyResult> CREATOR = new Creator<ImportKeyResult>() {
@@ -124,12 +133,12 @@ public class ImportKeyResult extends OperationResult {
if ((resultType & ImportKeyResult.RESULT_WARNINGS) > 0) {
duration = 0;
color = Style.ORANGE;
- withWarnings += activity.getString(R.string.import_with_warnings);
+ withWarnings += activity.getString(R.string.with_warnings);
}
if ((resultType & ImportKeyResult.RESULT_CANCELLED) > 0) {
duration = 0;
color = Style.ORANGE;
- withWarnings += activity.getString(R.string.import_with_cancelled);
+ withWarnings += activity.getString(R.string.with_cancelled);
}
// New and updated keys
@@ -162,8 +171,8 @@ public class ImportKeyResult extends OperationResult {
color = Style.RED;
if (isFailNothing()) {
str = activity.getString((resultType & ImportKeyResult.RESULT_CANCELLED) > 0
- ? R.string.import_error_nothing_cancelled
- : R.string.import_error_nothing);
+ ? R.string.import_error_nothing_cancelled
+ : R.string.import_error_nothing);
} else {
str = activity.getResources().getQuantityString(R.plurals.import_error, mBadKeys);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java
index 29924ee5d..e3f2c1cc5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java
@@ -93,6 +93,11 @@ public abstract class OperationResult implements Parcelable {
}
public OperationLog getLog() {
+ // If there is only a single entry, and it's a compound one, return that log
+ if (mLog.isSingleCompound()) {
+ return ((SubLogEntryParcel) mLog.getFirst()).getSubResult().getLog();
+ }
+ // Otherwse, return our regular log
return mLog;
}
@@ -106,7 +111,7 @@ public abstract class OperationResult implements Parcelable {
mType = type;
mParameters = parameters;
mIndent = indent;
- Log.v(Constants.TAG, "log: " + this.toString());
+ Log.v(Constants.TAG, "log: " + this);
}
public LogEntryParcel(Parcel source) {
@@ -122,6 +127,7 @@ public abstract class OperationResult implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(0);
dest.writeInt(mType.ordinal());
dest.writeSerializable(mParameters);
dest.writeInt(mIndent);
@@ -129,7 +135,12 @@ public abstract class OperationResult implements Parcelable {
public static final Creator<LogEntryParcel> CREATOR = new Creator<LogEntryParcel>() {
public LogEntryParcel createFromParcel(final Parcel source) {
- return new LogEntryParcel(source);
+ // Actually create LogEntryParcel or SubLogEntryParcel depending on type indicator
+ if (source.readInt() == 0) {
+ return new LogEntryParcel(source);
+ } else {
+ return new SubLogEntryParcel(source);
+ }
}
public LogEntryParcel[] newArray(final int size) {
@@ -139,7 +150,7 @@ public abstract class OperationResult implements Parcelable {
@Override
public String toString() {
- return "LogEntryParcel{" +
+ return getClass().getSimpleName() + "{" +
"mLevel=" + mType.mLevel +
", mType=" + mType +
", mParameters=" + Arrays.toString(mParameters) +
@@ -148,6 +159,42 @@ public abstract class OperationResult implements Parcelable {
}
}
+ public static class SubLogEntryParcel extends LogEntryParcel {
+
+ OperationResult mSubResult;
+
+ public SubLogEntryParcel(OperationResult subResult, LogType type, int indent, Object... parameters) {
+ super(type, indent, parameters);
+ mSubResult = subResult;
+
+ Log.v(Constants.TAG, "log: " + this);
+ }
+
+ public SubLogEntryParcel(Parcel source) {
+ super(source);
+ mSubResult = source.readParcelable(SubLogEntryParcel.class.getClassLoader());
+ }
+
+ public OperationResult getSubResult() {
+ return mSubResult;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(1);
+ dest.writeInt(mType.ordinal());
+ dest.writeSerializable(mParameters);
+ dest.writeInt(mIndent);
+ dest.writeParcelable(mSubResult, 0);
+ }
+
+ }
+
public SuperCardToast createNotify(final Activity activity) {
int color;
@@ -517,13 +564,36 @@ public abstract class OperationResult implements Parcelable {
MSG_SE_SIGCRYPTING (LogLevel.DEBUG, R.string.msg_se_sigcrypting),
MSG_SE_SYMMETRIC (LogLevel.INFO, R.string.msg_se_symmetric),
- MSG_CRT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_crt_upload_success),
+ MSG_CRT_CERTIFYING (LogLevel.DEBUG, R.string.msg_crt_certifying),
+ MSG_CRT_CERTIFY_ALL (LogLevel.DEBUG, R.string.msg_crt_certify_all),
+ MSG_CRT_CERTIFY_SOME (LogLevel.DEBUG, R.plurals.msg_crt_certify_some),
+ MSG_CRT_ERROR_MASTER_NOT_FOUND (LogLevel.ERROR, R.string.msg_crt_error_master_not_found),
+ MSG_CRT_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_crt_error_nothing),
+ MSG_CRT_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_crt_error_unlock),
+ MSG_CRT (LogLevel.START, R.string.msg_crt),
+ MSG_CRT_MASTER_FETCH (LogLevel.DEBUG, R.string.msg_crt_master_fetch),
+ MSG_CRT_SAVE (LogLevel.DEBUG, R.string.msg_crt_save),
+ MSG_CRT_SAVING (LogLevel.DEBUG, R.string.msg_crt_saving),
MSG_CRT_SUCCESS (LogLevel.OK, R.string.msg_crt_success),
+ MSG_CRT_UNLOCK (LogLevel.DEBUG, R.string.msg_crt_unlock),
+ MSG_CRT_WARN_NOT_FOUND (LogLevel.WARN, R.string.msg_crt_warn_not_found),
+ MSG_CRT_WARN_CERT_FAILED (LogLevel.WARN, R.string.msg_crt_warn_cert_failed),
+ MSG_CRT_WARN_SAVE_FAILED (LogLevel.WARN, R.string.msg_crt_warn_save_failed),
+
+ MSG_CRT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_crt_upload_success),
MSG_ACC_SAVED (LogLevel.INFO, R.string.api_settings_save_msg),
- MSG_NO_VALID_ENC (LogLevel.ERROR, R.string.error_invalid_data)
+ MSG_NO_VALID_ENC (LogLevel.ERROR, R.string.error_invalid_data),
+ // get key
+ MSG_GET_SUCCESS(LogLevel.OK, R.string.msg_download_success),
+ MSG_GET_NO_VALID_KEYS(LogLevel.ERROR, R.string.msg_download_no_valid_keys),
+ MSG_GET_NO_PGP_PARTS(LogLevel.ERROR, R.string.msg_download_no_pgp_parts),
+ MSG_GET_QUERY_TOO_SHORT(LogLevel.ERROR, R.string.msg_download_query_too_short),
+ MSG_GET_TOO_MANY_RESPONSES(LogLevel.ERROR, R.string.msg_download_too_many_responses),
+ MSG_GET_QUERY_TOO_SHORT_OR_TOO_MANY_RESPONSES(LogLevel.ERROR, R.string.msg_download_query_too_short_or_too_many_responses),
+ MSG_GET_QUERY_FAILED(LogLevel.ERROR, R.string.msg_download_query_failed)
;
public final int mMsgId;
@@ -574,6 +644,19 @@ public abstract class OperationResult implements Parcelable {
mParcels.add(new OperationResult.LogEntryParcel(type, indent, (Object[]) null));
}
+ public void add(OperationResult subResult, int indent) {
+ OperationLog subLog = subResult.getLog();
+ mParcels.add(new SubLogEntryParcel(subResult, subLog.getFirst().mType, indent, subLog.getFirst().mParameters));
+ }
+
+ boolean isSingleCompound() {
+ return mParcels.size() == 1 && getFirst() instanceof SubLogEntryParcel;
+ }
+
+ public void clear() {
+ mParcels.clear();
+ }
+
public boolean containsType(LogType type) {
for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
if (entry.mType == type) {
@@ -604,6 +687,13 @@ public abstract class OperationResult implements Parcelable {
return mParcels.isEmpty();
}
+ public LogEntryParcel getFirst() {
+ if (mParcels.isEmpty()) {
+ return null;
+ }
+ return mParcels.get(0);
+ }
+
public LogEntryParcel getLast() {
if (mParcels.isEmpty()) {
return null;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/AddKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/AddKeysActivity.java
new file mode 100644
index 000000000..427fc9315
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/AddKeysActivity.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.graphics.PorterDuff;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+import android.support.v4.util.LongSparseArray;
+import android.support.v7.app.ActionBarActivity;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.google.zxing.integration.android.IntentIntegrator;
+import com.google.zxing.integration.android.IntentResult;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.results.GetKeyResult;
+import org.sufficientlysecure.keychain.service.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.service.results.OperationResult;
+import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
+import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.widget.ExchangeKeySpinner;
+import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
+import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableFileCache;
+import org.sufficientlysecure.keychain.util.Preferences;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
+import edu.cmu.cylab.starslinger.exchange.ExchangeConfig;
+
+public class AddKeysActivity extends ActionBarActivity implements
+ LoaderManager.LoaderCallbacks<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
+
+ ExchangeKeySpinner mSafeSlingerKeySpinner;
+ View mActionSafeSlinger;
+ ImageView mActionSafeSlingerIcon;
+ View mActionQrCode;
+ View mActionNfc;
+
+ ProviderHelper mProviderHelper;
+
+ long mExchangeMasterKeyId = Constants.key.none;
+
+ byte[] mImportBytes;
+
+ private static final int REQUEST_CODE_RESULT = 0;
+ private static final int REQUEST_CODE_RESULT_TO_LIST = 1;
+ private static final int REQUEST_CODE_SAFE_SLINGER = 2;
+
+ private static final int LOADER_ID_BYTES = 0;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mProviderHelper = new ProviderHelper(this);
+
+ setContentView(R.layout.add_keys_activity);
+
+ mSafeSlingerKeySpinner = (ExchangeKeySpinner) findViewById(R.id.add_keys_safeslinger_key_spinner);
+ mActionSafeSlinger = findViewById(R.id.add_keys_safeslinger);
+ mActionSafeSlingerIcon = (ImageView) findViewById(R.id.add_keys_safeslinger_icon);
+ // make certify image gray, like action icons
+ mActionSafeSlingerIcon.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
+ PorterDuff.Mode.SRC_IN);
+ mActionQrCode = findViewById(R.id.add_keys_qr_code);
+ mActionNfc = findViewById(R.id.add_keys_nfc);
+
+ mSafeSlingerKeySpinner.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() {
+ @Override
+ public void onKeyChanged(long masterKeyId) {
+ mExchangeMasterKeyId = masterKeyId;
+ }
+ });
+
+ mActionSafeSlinger.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startExchange();
+ }
+ });
+
+ mActionQrCode.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startQrCode();
+ }
+ });
+
+ mActionNfc.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // show nfc help
+ Intent intent = new Intent(AddKeysActivity.this, HelpActivity.class);
+ intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, HelpActivity.TAB_NFC);
+ startActivityForResult(intent, REQUEST_CODE_RESULT);
+ }
+ });
+ }
+
+ private void startExchange() {
+ if (mExchangeMasterKeyId == 0) {
+ Notify.showNotify(this, getString(R.string.select_key_for_exchange),
+ Notify.Style.ERROR);
+ } else {
+ // retrieve public key blob and start SafeSlinger
+ Uri uri = KeychainContract.KeyRingData.buildPublicKeyRingUri(mExchangeMasterKeyId);
+ try {
+ byte[] keyBlob = (byte[]) mProviderHelper.getGenericData(
+ uri, KeychainContract.KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB);
+
+ Intent slingerIntent = new Intent(this, ExchangeActivity.class);
+ slingerIntent.putExtra(ExchangeConfig.extra.USER_DATA, keyBlob);
+ slingerIntent.putExtra(ExchangeConfig.extra.HOST_NAME, Constants.SAFESLINGER_SERVER);
+ startActivityForResult(slingerIntent, REQUEST_CODE_SAFE_SLINGER);
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG, "personal key not found", e);
+ }
+ }
+ }
+
+ private void startQrCode() {
+ // scan using xzing's Barcode Scanner
+ new IntentIntegrator(this).initiateScan();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_RESULT: {
+ if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(this).show();
+ }
+ break;
+ }
+ case REQUEST_CODE_RESULT_TO_LIST: {
+ // give it down...
+ setResult(0, data);
+ finish();
+ break;
+ }
+ case REQUEST_CODE_SAFE_SLINGER: {
+ switch (resultCode) {
+ case ExchangeActivity.RESULT_EXCHANGE_OK:
+ // import exchanged keys
+ mImportBytes = getSlingedKeys(data);
+ getSupportLoaderManager().restartLoader(LOADER_ID_BYTES, null, this);
+ break;
+ case ExchangeActivity.RESULT_EXCHANGE_CANCELED:
+ // do nothing
+ break;
+ }
+ break;
+ }
+ case IntentIntegratorSupportV4.REQUEST_CODE: {
+ IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
+ resultCode, data);
+ if (scanResult != null && scanResult.getFormatName() != null) {
+ String scannedContent = scanResult.getContents();
+
+ Log.d(Constants.TAG, "scannedContent: " + scannedContent);
+
+ // look if it's fingerprint only
+ if (scannedContent.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) {
+ importKeys(null, getFingerprintFromUri(Uri.parse(scanResult.getContents())));
+ return;
+ }
+
+ // is this a full key encoded as qr code?
+ if (scannedContent.startsWith("-----BEGIN PGP")) {
+ // TODO
+// mImportActivity.loadCallback(new ImportKeysListFragment.BytesLoaderState(scannedContent.getBytes(), null));
+ return;
+ }
+
+ // fail...
+ Notify.showNotify(this, R.string.import_qr_code_wrong, Notify.Style.ERROR);
+ }
+
+ break;
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ private String getFingerprintFromUri(Uri dataUri) {
+ String fingerprint = dataUri.toString().split(":")[1].toLowerCase(Locale.ENGLISH);
+ Log.d(Constants.TAG, "fingerprint: " + fingerprint);
+ return fingerprint;
+ }
+
+ private static byte[] getSlingedKeys(Intent data) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ Bundle extras = data.getExtras();
+ if (extras != null) {
+ byte[] d;
+ int i = 0;
+ do {
+ d = extras.getByteArray(ExchangeConfig.extra.MEMBER_DATA + i);
+ if (d != null) {
+ try {
+ out.write(d);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException", e);
+ }
+ i++;
+ }
+ } while (d != null);
+ }
+
+ return out.toByteArray();
+ }
+
+ @Override
+ public Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_ID_BYTES: {
+ InputData inputData = new InputData(new ByteArrayInputStream(mImportBytes), mImportBytes.length);
+ return new ImportKeysListLoader(this, inputData);
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader,
+ AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) {
+ Log.d(Constants.TAG, "data: " + data.getResult());
+
+ GetKeyResult getKeyResult = (GetKeyResult) data.getOperationResult();
+
+ LongSparseArray<ParcelableKeyRing> cachedKeyData = null;
+
+ // TODO: Use parcels!!!!!!!!!!!!!!!
+ switch (loader.getId()) {
+ case LOADER_ID_BYTES:
+
+ if (getKeyResult.success()) {
+ // No error
+ cachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
+ } else {
+ getKeyResult.createNotify(this).show();
+ }
+
+// if (error == null) {
+// // No error
+// cachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
+// Log.d(Constants.TAG, "no error!:" + cachedKeyData);
+//
+// } else if (error instanceof ImportKeysListLoader.NoValidKeysException) {
+// Notify.showNotify(this, R.string.error_import_no_valid_keys, Notify.Style.ERROR);
+// } else if (error instanceof ImportKeysListLoader.NonPgpPartException) {
+// Notify.showNotify(this,
+// ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources().
+// getQuantityString(R.plurals.error_import_non_pgp_part,
+// ((ImportKeysListLoader.NonPgpPartException) error).getCount()),
+// Notify.Style.OK
+// );
+// } else {
+// Notify.showNotify(this, R.string.error_generic_report_bug, Notify.Style.ERROR);
+// }
+ break;
+
+
+ default:
+ break;
+ }
+
+ importKeys(cachedKeyData, null);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader) {
+ switch (loader.getId()) {
+ case LOADER_ID_BYTES:
+ // Clear the data in the adapter.
+// mAdapter.clear();
+ break;
+ default:
+ break;
+ }
+ }
+
+ public ParcelableFileCache.IteratorWithSize<ParcelableKeyRing>
+ getSelectedData(final LongSparseArray<ParcelableKeyRing> keyData) {
+ return new ParcelableFileCache.IteratorWithSize<ParcelableKeyRing>() {
+ int i = 0;
+
+ @Override
+ public int getSize() {
+ return keyData.size();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return (i < getSize());
+ }
+
+ @Override
+ public ParcelableKeyRing next() {
+ // get the object by the key.
+ ParcelableKeyRing key = keyData.valueAt(i);
+ i++;
+ return key;
+ }
+
+ @Override
+ public void remove() {
+ keyData.remove(i);
+ }
+ };
+ }
+
+ public void importKeys(final LongSparseArray<ParcelableKeyRing> keyData, String fingerprint) {
+ // Message is received after importing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
+ this,
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL,
+ true) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+ if (returnData == null) {
+ return;
+ }
+ final ImportKeyResult result =
+ returnData.getParcelable(OperationResult.EXTRA_RESULT);
+ if (result == null) {
+ Log.e(Constants.TAG, "result == null");
+ return;
+ }
+
+ Intent certifyIntent = new Intent(AddKeysActivity.this, MultiCertifyKeyActivity.class);
+ certifyIntent.putExtra(MultiCertifyKeyActivity.EXTRA_RESULT, result);
+ certifyIntent.putExtra(MultiCertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
+ certifyIntent.putExtra(MultiCertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mExchangeMasterKeyId);
+ startActivityForResult(certifyIntent, REQUEST_CODE_RESULT_TO_LIST);
+ }
+ }
+ };
+
+ if (keyData != null) {
+ Log.d(Constants.TAG, "importKeys started");
+
+ // Send all information needed to service to import key in other thread
+ Intent intent = new Intent(this, KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ // instead of giving the entries by Intent extra, cache them into a
+ // file to prevent Java Binder problems on heavy imports
+ // read FileImportCache for more info.
+ try {
+ // We parcel this iteratively into a file - anything we can
+ // display here, we should be able to import.
+ ParcelableFileCache<ParcelableKeyRing> cache =
+ new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl");
+ cache.writeCache(getSelectedData(keyData));
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Problem writing cache file", e);
+ Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR);
+ }
+ } else if (fingerprint != null) {
+
+ // search config
+ Preferences prefs = Preferences.getPreferences(this);
+ Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
+
+ // Send all information needed to service to query keys in other thread
+ Intent intent = new Intent(this, KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_DOWNLOAD_AND_IMPORT_KEYS);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, cloudPrefs.keyserver);
+
+ final ImportKeysListEntry keyEntry = new ImportKeysListEntry();
+ keyEntry.setFingerprintHex(fingerprint);
+ keyEntry.addOrigin(cloudPrefs.keyserver);
+ ArrayList<ImportKeysListEntry> selectedEntries = new ArrayList<ImportKeysListEntry>();
+ selectedEntries.add(keyEntry);
+
+ data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+ } else {
+ Notify.showNotify(this, R.string.error_nothing_import, Notify.Style.ERROR);
+ }
+ }
+}
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 5eec8454a..0c3eeece4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
@@ -48,6 +48,10 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
+import org.sufficientlysecure.keychain.service.results.CertifyResult;
+import org.sufficientlysecure.keychain.service.results.SingletonResult;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -55,9 +59,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.service.results.OperationResult.LogLevel;
import org.sufficientlysecure.keychain.service.results.OperationResult.LogType;
-import org.sufficientlysecure.keychain.service.results.SingletonResult;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
@@ -243,8 +245,8 @@ public class CertifyKeyFragment extends LoaderFragment
String mainUserId = data.getString(INDEX_USER_ID);
mInfoPrimaryUserId.setText(mainUserId);
- byte[] fingerprintBlob = data.getBlob(INDEX_FINGERPRINT);
- String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob);
+ byte[] fp = data.getBlob(INDEX_FINGERPRINT);
+ String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fp);
mInfoFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint));
}
break;
@@ -312,27 +314,27 @@ public class CertifyKeyFragment extends LoaderFragment
intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING);
// fill values for this action
- Bundle data = new Bundle();
-
- data.putLong(KeychainIntentService.CERTIFY_KEY_MASTER_KEY_ID, mMasterKeyId);
- data.putLong(KeychainIntentService.CERTIFY_KEY_PUB_KEY_ID, mPubKeyId);
- data.putStringArrayList(KeychainIntentService.CERTIFY_KEY_UIDS, userIds);
+ CertifyActionsParcel parcel = new CertifyActionsParcel(mMasterKeyId);
+ parcel.add(new CertifyAction(mPubKeyId, userIds));
+ Bundle data = new Bundle();
+ data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Message is received after signing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity,
- getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER) {
+ getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- SingletonResult result = new SingletonResult(
- SingletonResult.RESULT_OK, LogType.MSG_CRT_SUCCESS);
+ Bundle data = message.getData();
+ CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
+
Intent intent = new Intent();
- intent.putExtra(SingletonResult.EXTRA_RESULT, result);
+ intent.putExtra(CertifyResult.EXTRA_RESULT, result);
mActivity.setResult(CertifyKeyActivity.RESULT_OK, intent);
// check if we need to send the key to the server or not
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
index 6f521efa2..b7d204851 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -21,16 +21,14 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
import android.widget.TextView;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -43,14 +41,19 @@ public abstract class DecryptFragment extends Fragment {
protected long mSignatureKeyId = 0;
protected LinearLayout mResultLayout;
- protected RelativeLayout mSignatureLayout;
- protected TextView mResultText;
- protected ImageView mSignatureStatusImage;
- protected TextView mUserId;
- protected TextView mUserIdRest;
+ protected ImageView mEncryptionIcon;
+ protected TextView mEncryptionText;
+ protected ImageView mSignatureIcon;
+ protected TextView mSignatureText;
+
+ protected View mSignatureLayout;
+ protected View mSignatureDivider1;
+ protected View mSignatureDivider2;
+ protected TextView mSignatureName;
+ protected TextView mSignatureEmail;
+ protected TextView mSignatureAction;
- protected Button mLookupKey;
// State
protected String mPassphrase;
@@ -60,20 +63,20 @@ public abstract class DecryptFragment extends Fragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mResultLayout = (LinearLayout) getView().findViewById(R.id.result);
- mResultText = (TextView) getView().findViewById(R.id.result_text);
- mSignatureLayout = (RelativeLayout) getView().findViewById(R.id.result_signature);
- mSignatureStatusImage = (ImageView) getView().findViewById(R.id.ic_signature_status);
- mUserId = (TextView) getView().findViewById(R.id.mainUserId);
- mUserIdRest = (TextView) getView().findViewById(R.id.mainUserIdRest);
- mLookupKey = (Button) getView().findViewById(R.id.lookup_key);
- mLookupKey.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- lookupUnknownKey(mSignatureKeyId);
- }
- });
+ mResultLayout = (LinearLayout) getView().findViewById(R.id.result_main_layout);
mResultLayout.setVisibility(View.GONE);
+
+ mEncryptionIcon = (ImageView) getView().findViewById(R.id.result_encryption_icon);
+ mEncryptionText = (TextView) getView().findViewById(R.id.result_encryption_text);
+ mSignatureIcon = (ImageView) getView().findViewById(R.id.result_signature_icon);
+ mSignatureText = (TextView) getView().findViewById(R.id.result_signature_text);
+ mSignatureLayout = getView().findViewById(R.id.result_signature_layout);
+ mSignatureDivider1 = getView().findViewById(R.id.result_signature_divider1);
+ mSignatureDivider2 = getView().findViewById(R.id.result_signature_divider2);
+ mSignatureName = (TextView) getView().findViewById(R.id.result_signature_name);
+ mSignatureEmail = (TextView) getView().findViewById(R.id.result_signature_email);
+ mSignatureAction = (TextView) getView().findViewById(R.id.result_signature_action);
+
}
private void lookupUnknownKey(long unknownKeyId) {
@@ -83,6 +86,13 @@ public abstract class DecryptFragment extends Fragment {
startActivityForResult(intent, RESULT_CODE_LOOKUP_KEY);
}
+ private void showKey(long keyId) {
+ Intent viewKeyIntent = new Intent(getActivity(), ViewKeyActivity.class);
+ viewKeyIntent.setData(KeychainContract.KeyRings
+ .buildGenericKeyRingUri(keyId));
+ startActivity(viewKeyIntent);
+ }
+
protected void startPassphraseDialog(long subkeyId) {
Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId);
@@ -102,100 +112,143 @@ public abstract class DecryptFragment extends Fragment {
startActivityForResult(intent, REQUEST_CODE_NFC_DECRYPT);
}
- protected void onResult(DecryptVerifyResult decryptVerifyResult) {
- OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
+ /**
+ *
+ * @return returns false if signature is invalid, key is revoked or expired.
+ */
+ protected boolean onResult(DecryptVerifyResult decryptVerifyResult) {
+ final OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
+
+ boolean valid = false;
mSignatureKeyId = 0;
mResultLayout.setVisibility(View.VISIBLE);
if (signatureResult != null) {
- mSignatureStatusImage.setVisibility(View.VISIBLE);
-
mSignatureKeyId = signatureResult.getKeyId();
String userId = signatureResult.getPrimaryUserId();
String[] userIdSplit = KeyRing.splitUserId(userId);
if (userIdSplit[0] != null) {
- mUserId.setText(userIdSplit[0]);
+ mSignatureName.setText(userIdSplit[0]);
} else {
- mUserId.setText(R.string.user_id_no_name);
+ mSignatureName.setText(R.string.user_id_no_name);
}
if (userIdSplit[1] != null) {
- mUserIdRest.setText(userIdSplit[1]);
+ mSignatureEmail.setText(userIdSplit[1]);
+ } else {
+ mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), mSignatureKeyId));
+ }
+
+ if (signatureResult.isSignatureOnly()) {
+ mEncryptionText.setText(R.string.decrypt_result_not_encrypted);
+ KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, KeyFormattingUtils.STATE_NOT_ENCRYPTED);
} else {
- mUserIdRest.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), mSignatureKeyId));
+ mEncryptionText.setText(R.string.decrypt_result_encrypted);
+ KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, KeyFormattingUtils.STATE_ENCRYPTED);
}
switch (signatureResult.getStatus()) {
case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {
- if (signatureResult.isSignatureOnly()) {
- mResultText.setText(R.string.decrypt_result_signature_certified);
- } else {
- mResultText.setText(R.string.decrypt_result_decrypted_and_signature_certified);
- }
-
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_green_light));
- mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
- mSignatureLayout.setVisibility(View.VISIBLE);
- mLookupKey.setVisibility(View.GONE);
+ mSignatureText.setText(R.string.decrypt_result_signature_certified);
+ KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_VERIFIED);
+
+ setSignatureLayoutVisibility(View.VISIBLE);
+ setShowAction(mSignatureKeyId);
+
+ valid = true;
break;
}
case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {
- if (signatureResult.isSignatureOnly()) {
- mResultText.setText(R.string.decrypt_result_signature_uncertified);
- } else {
- mResultText.setText(R.string.decrypt_result_decrypted_and_signature_uncertified);
- }
-
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_orange_light));
- mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
- mSignatureLayout.setVisibility(View.VISIBLE);
- mLookupKey.setVisibility(View.GONE);
+ mSignatureText.setText(R.string.decrypt_result_signature_uncertified);
+ KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_UNVERIFIED);
+
+ setSignatureLayoutVisibility(View.VISIBLE);
+ setShowAction(mSignatureKeyId);
+
+ valid = true;
break;
}
case OpenPgpSignatureResult.SIGNATURE_KEY_MISSING: {
- if (signatureResult.isSignatureOnly()) {
- mResultText.setText(R.string.decrypt_result_signature_unknown_pub_key);
- } else {
- mResultText.setText(R.string.decrypt_result_decrypted_unknown_pub_key);
- }
-
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_orange_light));
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- mSignatureLayout.setVisibility(View.VISIBLE);
- mLookupKey.setVisibility(View.VISIBLE);
+ mSignatureText.setText(R.string.decrypt_result_signature_missing_key);
+ KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_UNKNOWN_KEY);
+
+ setSignatureLayoutVisibility(View.VISIBLE);
+ mSignatureAction.setText(R.string.decrypt_result_action_Lookup);
+ mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_download, 0);
+ mSignatureLayout.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ lookupUnknownKey(mSignatureKeyId);
+ }
+ });
+
+ valid = true;
break;
}
- case OpenPgpSignatureResult.SIGNATURE_ERROR: {
- mResultText.setText(R.string.decrypt_result_invalid_signature);
+ case OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED: {
+ mSignatureText.setText(R.string.decrypt_result_signature_expired_key);
+ KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_EXPIRED);
+
+ setSignatureLayoutVisibility(View.VISIBLE);
+ setShowAction(mSignatureKeyId);
+
+ valid = false;
+ break;
+ }
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_red_light));
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- mSignatureLayout.setVisibility(View.GONE);
- mLookupKey.setVisibility(View.GONE);
+ case OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED: {
+ mSignatureText.setText(R.string.decrypt_result_signature_revoked_key);
+ KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_REVOKED);
+
+ setSignatureLayoutVisibility(View.VISIBLE);
+ setShowAction(mSignatureKeyId);
+
+ valid = false;
break;
}
- default: {
- mResultText.setText(R.string.error);
+ case OpenPgpSignatureResult.SIGNATURE_ERROR: {
+ mSignatureText.setText(R.string.decrypt_result_invalid_signature);
+ KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_INVALID);
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_red_light));
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- mSignatureLayout.setVisibility(View.GONE);
- mLookupKey.setVisibility(View.GONE);
+ setSignatureLayoutVisibility(View.GONE);
+
+ valid = false;
break;
}
}
} else {
- mSignatureLayout.setVisibility(View.GONE);
- mLookupKey.setVisibility(View.GONE);
+ setSignatureLayoutVisibility(View.GONE);
+
+ mSignatureText.setText(R.string.decrypt_result_no_signature);
+ KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_NOT_SIGNED);
+ mEncryptionText.setText(R.string.decrypt_result_encrypted);
+ KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, KeyFormattingUtils.STATE_ENCRYPTED);
- // successful decryption-only
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_purple_light));
- mResultText.setText(R.string.decrypt_result_decrypted);
+ valid = true;
}
+
+ return valid;
+ }
+
+ private void setSignatureLayoutVisibility(int visibility) {
+ mSignatureLayout.setVisibility(visibility);
+ mSignatureDivider1.setVisibility(visibility);
+ mSignatureDivider2.setVisibility(visibility);
+ }
+
+ private void setShowAction(final long signatureKeyId) {
+ mSignatureAction.setText(R.string.decrypt_result_action_show);
+ mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_accounts, 0);
+ mSignatureLayout.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showKey(signatureKeyId);
+ }
+ });
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java
index 73163eabd..28010884b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
+import android.text.TextUtils;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -78,29 +79,41 @@ public class DecryptTextActivity extends ActionBarActivity {
/**
* Fixing broken PGP SIGNED MESSAGE Strings coming from GMail/AOSP Mail
*/
- private String fixPgpCleartextSignature(String message) {
- // windows newline -> unix newline
- message = message.replaceAll("\r\n", "\n");
- // Mac OS before X newline -> unix newline
- message = message.replaceAll("\r", "\n");
+ private String fixPgpCleartextSignature(CharSequence input) {
+ if (!TextUtils.isEmpty(input)) {
+ String text = input.toString();
- return message;
+ // windows newline -> unix newline
+ text = text.replaceAll("\r\n", "\n");
+ // Mac OS before X newline -> unix newline
+ text = text.replaceAll("\r", "\n");
+
+ return text;
+ } else {
+ return null;
+ }
}
- private String getPgpContent(String input) {
+ private String getPgpContent(CharSequence input) {
// only decrypt if clipboard content is available and a pgp message or cleartext signature
- if (input != null) {
+ if (!TextUtils.isEmpty(input)) {
+ Log.dEscaped(Constants.TAG, "input: " + input);
+
Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(input);
if (matcher.matches()) {
- String message = matcher.group(1);
- message = fixPgpMessage(message);
- return message;
+ String text = matcher.group(1);
+ text = fixPgpMessage(text);
+
+ Log.dEscaped(Constants.TAG, "input fixed: " + text);
+ return text;
} else {
matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(input);
if (matcher.matches()) {
- String message = matcher.group(1);
- message = fixPgpCleartextSignature(message);
- return message;
+ String text = matcher.group(1);
+ text = fixPgpCleartextSignature(text);
+
+ Log.dEscaped(Constants.TAG, "input fixed: " + text);
+ return text;
} else {
return null;
}
@@ -125,14 +138,13 @@ public class DecryptTextActivity extends ActionBarActivity {
}
if (Intent.ACTION_SEND.equals(action) && type != null) {
- Log.logDebugBundle(extras, "extras");
+ Log.d(Constants.TAG, "ACTION_SEND");
+ Log.logDebugBundle(extras, "SEND extras");
// When sending to Keychain Decrypt via share menu
if ("text/plain".equals(type)) {
String sharedText = extras.getString(Intent.EXTRA_TEXT);
- Log.dEscaped(Constants.TAG, "sharedText incoming: " + sharedText);
sharedText = getPgpContent(sharedText);
- Log.dEscaped(Constants.TAG, "sharedText fixed: " + sharedText);
if (sharedText != null) {
loadFragment(savedInstanceState, sharedText);
@@ -143,7 +155,7 @@ public class DecryptTextActivity extends ActionBarActivity {
Log.e(Constants.TAG, "ACTION_SEND received non-plaintext, this should not happen in this activity!");
}
} else if (ACTION_DECRYPT_TEXT.equals(action)) {
- Log.d(Constants.TAG, "ACTION_DECRYPT_TEXT textData not null, matching text...");
+ Log.d(Constants.TAG, "ACTION_DECRYPT_TEXT");
String extraText = extras.getString(EXTRA_TEXT);
extraText = getPgpContent(extraText);
@@ -157,9 +169,9 @@ public class DecryptTextActivity extends ActionBarActivity {
Log.d(Constants.TAG, "ACTION_DECRYPT_FROM_CLIPBOARD");
CharSequence clipboardText = ClipboardReflection.getClipboardText(this);
+ String text = getPgpContent(clipboardText);
- if (clipboardText != null) {
- String text = getPgpContent(clipboardText.toString());
+ if (text != null) {
loadFragment(savedInstanceState, text);
} else {
returnInvalidResult();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
index 4f25126ee..78345e11c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
@@ -27,6 +27,8 @@ import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
import android.widget.TextView;
import org.openintents.openpgp.util.OpenPgpApi;
@@ -44,6 +46,9 @@ public class DecryptTextFragment extends DecryptFragment {
public static final String ARG_CIPHERTEXT = "ciphertext";
// view
+ private LinearLayout mValidLayout;
+ private LinearLayout mInvalidLayout;
+ private Button mInvalidButton;
private TextView mText;
private View mShareButton;
private View mCopyButton;
@@ -71,7 +76,9 @@ public class DecryptTextFragment extends DecryptFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.decrypt_text_fragment, container, false);
-
+ mValidLayout = (LinearLayout) view.findViewById(R.id.decrypt_text_valid);
+ mInvalidLayout = (LinearLayout) view.findViewById(R.id.decrypt_text_invalid);
+ mInvalidButton = (Button) view.findViewById(R.id.decrypt_text_invalid_button);
mText = (TextView) view.findViewById(R.id.decrypt_text_plaintext);
mShareButton = view.findViewById(R.id.action_decrypt_share_plaintext);
mCopyButton = view.findViewById(R.id.action_decrypt_copy_plaintext);
@@ -87,6 +94,13 @@ public class DecryptTextFragment extends DecryptFragment {
copyToClipboard(mText.getText().toString());
}
});
+ mInvalidButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mInvalidLayout.setVisibility(View.GONE);
+ mValidLayout.setVisibility(View.VISIBLE);
+ }
+ });
return view;
}
@@ -186,9 +200,18 @@ public class DecryptTextFragment extends DecryptFragment {
pgpResult.createNotify(getActivity()).show();
// display signature result in activity
- onResult(pgpResult);
+ boolean valid = onResult(pgpResult);
+
+ if (valid) {
+ mInvalidLayout.setVisibility(View.GONE);
+ mValidLayout.setVisibility(View.VISIBLE);
+ } else {
+ mInvalidLayout.setVisibility(View.VISIBLE);
+ mValidLayout.setVisibility(View.GONE);
+ }
} else {
pgpResult.createNotify(getActivity()).show();
+ // TODO: show also invalid layout with different text?
}
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
index 7132518ae..da46de486 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -82,7 +82,7 @@ public class DrawerActivity extends ActionBarActivity {
}
NavItem mItemIconTexts[] = new NavItem[]{
- new NavItem(R.drawable.ic_action_person, getString(R.string.nav_keys)),
+ new NavItem(R.drawable.ic_action_accounts, getString(R.string.nav_keys)),
new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_text)),
new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_files)),
new NavItem(R.drawable.ic_action_not_secure, getString(R.string.nav_decrypt)),
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index efad2e45d..f0bd2c76c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -97,7 +97,6 @@ public class ImportKeysActivity extends ActionBarActivity {
private SlidingTabLayout mSlidingTabLayout;
private PagerTabStripAdapter mTabsAdapter;
- public static final int VIEW_PAGER_HEIGHT = 64; // dp
private static final int ALL_TABS = -1;
private static final int TAB_CLOUD = 0;
@@ -151,12 +150,13 @@ public class ImportKeysActivity extends ActionBarActivity {
}
Bundle serverBundle = null;
- int showTabOnly = ALL_TABS;
+ int showTabOnly = TAB_CLOUD;
if (ACTION_IMPORT_KEY.equals(action)) {
/* Keychain's own Actions */
// display file fragment
- mViewPager.setCurrentItem(TAB_FILE);
+ showTabOnly = TAB_FILE;
+ mSwitchToTab = TAB_FILE;
if (dataUri != null) {
// action: directly load data
@@ -230,6 +230,7 @@ public class ImportKeysActivity extends ActionBarActivity {
}
} else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
// NOTE: this only displays the appropriate fragment, no actions are taken
+ showTabOnly = TAB_FILE;
mSwitchToTab = TAB_FILE;
// no immediate actions!
@@ -244,14 +245,14 @@ public class ImportKeysActivity extends ActionBarActivity {
startListFragment(savedInstanceState, null, null, null);
} else if (ACTION_IMPORT_KEY_FROM_QR_CODE.equals(action)) {
// also exposed in AndroidManifest
-
+ showTabOnly = ALL_TABS;
// NOTE: this only displays the appropriate fragment, no actions are taken
mSwitchToTab = TAB_QR_CODE;
// no immediate actions!
startListFragment(savedInstanceState, null, null, null);
} else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) {
-
+ showTabOnly = ALL_TABS;
// NOTE: this only displays the appropriate fragment, no actions are taken
mSwitchToTab = TAB_QR_CODE;
@@ -270,10 +271,6 @@ public class ImportKeysActivity extends ActionBarActivity {
mSlidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- // resize view pager back to 64 if keyserver settings have been collapsed
- if (getViewPagerHeight() > VIEW_PAGER_HEIGHT) {
- resizeViewPager(VIEW_PAGER_HEIGHT);
- }
}
@Override
@@ -339,44 +336,36 @@ public class ImportKeysActivity extends ActionBarActivity {
getSupportFragmentManager().executePendingTransactions();
}
- public void resizeViewPager(int dp) {
- ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
- params.height = FormattingUtils.dpToPx(this, dp);
- // update layout after operations
- mSlidingTabLayout.setViewPager(mViewPager);
- }
-
- public int getViewPagerHeight() {
- ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
- return FormattingUtils.pxToDp(this, params.height);
- }
-
- private String getFingerprintFromUri(Uri dataUri) {
+ public static String getFingerprintFromUri(Uri dataUri) {
String fingerprint = dataUri.toString().split(":")[1].toLowerCase(Locale.ENGLISH);
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
return fingerprint;
}
- public void loadFromFingerprintUri(Uri dataUri) {
- String query = "0x" + getFingerprintFromUri(dataUri);
+ public void loadFromFingerprint(String fingerprint) {
+// String fingerprint = "0x" + getFingerprintFromUri(dataUri);
// setCurrentItem does not work directly after onResume (from qr code scanner)
// see http://stackoverflow.com/q/19316729
// so, reset adapter completely!
- if (mViewPager.getAdapter() != null)
- mViewPager.setAdapter(null);
- mViewPager.setAdapter(mTabsAdapter);
- mViewPager.setCurrentItem(TAB_CLOUD);
-
- ImportKeysCloudFragment f = (ImportKeysCloudFragment)
- getActiveFragment(mViewPager, TAB_CLOUD);
-
- // search config
- Preferences prefs = Preferences.getPreferences(ImportKeysActivity.this);
- Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
-
- // search directly
- loadCallback(new ImportKeysListFragment.CloudLoaderState(query, cloudPrefs));
+// if (mViewPager.getAdapter() != null)
+// mViewPager.setAdapter(null);
+// mViewPager.setAdapter(mTabsAdapter);
+// mViewPager.setCurrentItem(TAB_CLOUD);
+
+// ImportKeysCloudFragment f = (ImportKeysCloudFragment)
+// getActiveFragment(mViewPager, TAB_CLOUD);
+
+ Intent searchIntent = new Intent(this, ImportKeysActivity.class);
+ searchIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint);
+ startActivity(searchIntent);
+
+// // search config
+// Preferences prefs = Preferences.getPreferences(ImportKeysActivity.this);
+// Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
+//
+// // search directly
+// loadCallback(new ImportKeysListFragment.CloudLoaderState(query, cloudPrefs));
}
// http://stackoverflow.com/a/9293207
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
index e5bad16ce..538fa16c7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
@@ -31,8 +31,6 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.util.FileHelper;
-import java.util.Locale;
-
public class ImportKeysFileFragment extends Fragment {
private ImportKeysActivity mImportActivity;
private View mBrowse;
@@ -80,12 +78,8 @@ public class ImportKeysFileFragment extends Fragment {
String sendText = "";
if (clipboardText != null) {
sendText = clipboardText.toString();
- if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) {
- mImportActivity.loadFromFingerprintUri(Uri.parse(sendText));
- return;
- }
+ mImportActivity.loadCallback(new ImportKeysListFragment.BytesLoaderState(sendText.getBytes(), null));
}
- mImportActivity.loadCallback(new ImportKeysListFragment.BytesLoaderState(sendText.getBytes(), null));
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index 16ebd6aca..ad50ecd3d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -31,6 +31,7 @@ import android.widget.ListView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.results.GetKeyResult;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
@@ -285,48 +286,60 @@ public class ImportKeysListFragment extends ListFragment implements
setListShownNoAnimation(true);
}
- Exception error = data.getError();
-
// free old cached key data
mCachedKeyData = null;
+ GetKeyResult getKeyResult = (GetKeyResult) data.getOperationResult();
switch (loader.getId()) {
case LOADER_ID_BYTES:
- if (error == null) {
+ if (getKeyResult.success()) {
// No error
mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
- } else if (error instanceof ImportKeysListLoader.NoValidKeysException) {
- Notify.showNotify(getActivity(), R.string.error_import_no_valid_keys, Notify.Style.ERROR);
- } else if (error instanceof ImportKeysListLoader.NonPgpPartException) {
- Notify.showNotify(getActivity(),
- ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources().
- getQuantityString(R.plurals.error_import_non_pgp_part,
- ((ImportKeysListLoader.NonPgpPartException) error).getCount()),
- Notify.Style.OK
- );
} else {
- Notify.showNotify(getActivity(), R.string.error_generic_report_bug, Notify.Style.ERROR);
+ getKeyResult.createNotify(getActivity()).show();
}
+// if (error == null) {
+// // No error
+// mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
+// } else if (error instanceof ImportKeysListLoader.NoValidKeysException) {
+// Notify.showNotify(getActivity(), R.string.error_import_no_valid_keys, Notify.Style.ERROR);
+// } else if (error instanceof ImportKeysListLoader.NonPgpPartException) {
+// Notify.showNotify(getActivity(),
+// ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources().
+// getQuantityString(R.plurals.error_import_non_pgp_part,
+// ((ImportKeysListLoader.NonPgpPartException) error).getCount()),
+// Notify.Style.OK
+// );
+// } else {
+// Notify.showNotify(getActivity(), R.string.error_generic_report_bug, Notify.Style.ERROR);
+// }
break;
case LOADER_ID_CLOUD:
- if (error == null) {
+ if (getKeyResult.success()) {
// No error
- } else if (error instanceof Keyserver.QueryTooShortException) {
- Notify.showNotify(getActivity(), R.string.error_query_too_short, Notify.Style.ERROR);
- } else if (error instanceof Keyserver.TooManyResponsesException) {
- Notify.showNotify(getActivity(), R.string.error_too_many_responses, Notify.Style.ERROR);
- } else if (error instanceof Keyserver.QueryTooShortOrTooManyResponsesException) {
- Notify.showNotify(getActivity(), R.string.error_too_short_or_too_many_responses, Notify.Style.ERROR);
- } else if (error instanceof Keyserver.QueryFailedException) {
- Log.d(Constants.TAG,
- "Unrecoverable keyserver query error: " + error.getLocalizedMessage());
- String alert = getActivity().getString(R.string.error_searching_keys);
- alert = alert + " (" + error.getLocalizedMessage() + ")";
- Notify.showNotify(getActivity(), alert, Notify.Style.ERROR);
+ } else {
+ getKeyResult.createNotify(getActivity()).show();
}
+
+
+// if (error == null) {
+// // No error
+// } else if (error instanceof Keyserver.QueryTooShortException) {
+// Notify.showNotify(getActivity(), R.string.error_query_too_short, Notify.Style.ERROR);
+// } else if (error instanceof Keyserver.TooManyResponsesException) {
+// Notify.showNotify(getActivity(), R.string.error_too_many_responses, Notify.Style.ERROR);
+// } else if (error instanceof Keyserver.QueryTooShortOrTooManyResponsesException) {
+// Notify.showNotify(getActivity(), R.string.error_too_short_or_too_many_responses, Notify.Style.ERROR);
+// } else if (error instanceof Keyserver.QueryFailedException) {
+// Log.d(Constants.TAG,
+// "Unrecoverable keyserver query error: " + error.getLocalizedMessage());
+// String alert = getActivity().getString(R.string.error_searching_keys);
+// alert = alert + " (" + error.getLocalizedMessage() + ")";
+// Notify.showNotify(getActivity(), alert, Notify.Style.ERROR);
+// }
break;
default:
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
index a52737979..fb4bbfac4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
@@ -132,7 +132,7 @@ public class ImportKeysQrCodeFragment extends Fragment {
}
public void importFingerprint(Uri dataUri) {
- mImportActivity.loadFromFingerprintUri(dataUri);
+ mImportActivity.loadFromFingerprint(ImportKeysActivity.getFingerprintFromUri(dataUri));
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index f06531b01..019ee82ec 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -28,16 +28,16 @@ import android.view.MenuItem;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.ExportHelper;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.results.OperationResult;
import org.sufficientlysecure.keychain.service.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.service.results.OperationResult;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.util.ExportHelper;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException;
@@ -49,6 +49,8 @@ public class KeyListActivity extends DrawerActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setTitle(R.string.nav_keys);
+
// if this is the first time show first time activity
Preferences prefs = Preferences.getPreferences(this);
if (prefs.isFirstTime()) {
@@ -84,7 +86,11 @@ public class KeyListActivity extends DrawerActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_key_list_add:
- importKeys();
+ addKeys();
+ return true;
+
+ case R.id.menu_key_list_search_cloud:
+ searchCloud();
return true;
case R.id.menu_key_list_create:
@@ -139,11 +145,17 @@ public class KeyListActivity extends DrawerActivity {
}
}
- private void importKeys() {
- Intent intent = new Intent(this, ImportKeysActivity.class);
+ private void addKeys() {
+ Intent intent = new Intent(this, AddKeysActivity.class);
startActivityForResult(intent, 0);
}
+ private void searchCloud() {
+ Intent importIntent = new Intent(this, ImportKeysActivity.class);
+ importIntent.putExtra(ImportKeysActivity.EXTRA_QUERY, (String) null); // hack to show only cloud tab
+ startActivity(importIntent);
+ }
+
private void createKey() {
Intent intent = new Intent(this, CreateKeyActivity.class);
startActivityForResult(intent, 0);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index 02397538c..4024ee7fe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -49,9 +49,11 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
+import android.widget.Spinner;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
@@ -59,7 +61,6 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.KeyUpdateHelper;
-import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@@ -69,8 +70,10 @@ import org.sufficientlysecure.keychain.ui.util.Highlighter;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
+import java.util.List;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
@@ -86,10 +89,13 @@ public class KeyListFragment extends LoaderFragment
private KeyListAdapter mAdapter;
private StickyListHeadersListView mStickyList;
private ListAwareSwipeRefreshLayout mSwipeRefreshLayout;
+ private Spinner mFilterSpinner;
// saves the mode object for multiselect, needed for reset at some point
private ActionMode mActionMode = null;
+ private boolean mShowAllKeys = false;
+
private String mQuery;
private SearchView mSearchView;
// empty list layout
@@ -214,6 +220,42 @@ public class KeyListFragment extends LoaderFragment
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ mFilterSpinner = (Spinner) getActivity().findViewById(R.id.key_list_filter_spinner);
+ List<String> list = new ArrayList<String>();
+ list.add(getString(R.string.key_list_filter_show_certified));
+ list.add(getString(R.string.key_list_filter_show_all));
+
+ ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>
+ (getActivity(), android.R.layout.simple_spinner_item, list);
+
+ dataAdapter.setDropDownViewResource
+ (android.R.layout.simple_spinner_dropdown_item);
+
+ mFilterSpinner.setAdapter(dataAdapter);
+
+ mFilterSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ switch (position) {
+ case 0: {
+ mShowAllKeys = false;
+ getLoaderManager().restartLoader(0, null, KeyListFragment.this);
+ break;
+ }
+ case 1: {
+ mShowAllKeys = true;
+ getLoaderManager().restartLoader(0, null, KeyListFragment.this);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+
+ }
+ });
+
mStickyList.setOnItemClickListener(this);
mStickyList.setAreHeadersSticky(true);
mStickyList.setDrawingListUnderStickyHeader(false);
@@ -356,6 +398,14 @@ public class KeyListFragment extends LoaderFragment
whereArgs[i] = "%" + words[i] + "%";
}
}
+ if (!mShowAllKeys) {
+ if (where == null) {
+ where = "";
+ } else {
+ where += " AND ";
+ }
+ where += KeyRings.VERIFIED + " != 0";
+ }
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
@@ -471,6 +521,11 @@ public class KeyListFragment extends LoaderFragment
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ hideMenu = true;
+ getActivity().invalidateOptionsMenu();
+ }
+
// disable swipe-to-refresh
// mSwipeRefreshLayout.setIsLocked(true);
return true;
@@ -480,6 +535,11 @@ public class KeyListFragment extends LoaderFragment
public boolean onMenuItemActionCollapse(MenuItem item) {
mQuery = null;
getLoaderManager().restartLoader(0, null, KeyListFragment.this);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ hideMenu = false;
+ getActivity().invalidateOptionsMenu();
+ }
// enable swipe-to-refresh
// mSwipeRefreshLayout.setIsLocked(false);
return true;
@@ -553,7 +613,7 @@ public class KeyListFragment extends LoaderFragment
ItemViewHolder holder = new ItemViewHolder();
holder.mMainUserId = (TextView) view.findViewById(R.id.mainUserId);
holder.mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- holder.mStatus = (ImageView) view.findViewById(R.id.status_image);
+ holder.mStatus = (ImageView) view.findViewById(R.id.status_icon);
view.setTag(holder);
return view;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
index 4fdfd3a21..0f59b6d6a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
@@ -24,36 +24,27 @@ import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.TypedValue;
-import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnTouchListener;
import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.results.OperationResult;
import org.sufficientlysecure.keychain.service.results.OperationResult.LogEntryParcel;
import org.sufficientlysecure.keychain.service.results.OperationResult.LogLevel;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.service.results.OperationResult.SubLogEntryParcel;
-import java.util.HashMap;
+public class LogDisplayFragment extends ListFragment implements OnItemClickListener {
-public class LogDisplayFragment extends ListFragment implements OnTouchListener {
-
- HashMap<LogLevel,LogAdapter> mAdapters = new HashMap<LogLevel, LogAdapter>();
LogAdapter mAdapter;
- LogLevel mLevel = LogLevel.DEBUG;
OperationResult mResult;
- GestureDetector mDetector;
-
public static final String EXTRA_RESULT = "log";
@Override
@@ -72,58 +63,25 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener
return;
}
- mAdapter = new LogAdapter(getActivity(), mResult.getLog(), LogLevel.DEBUG);
- mAdapters.put(LogLevel.DEBUG, mAdapter);
+ mAdapter = new LogAdapter(getActivity(), mResult.getLog());
setListAdapter(mAdapter);
- mDetector = new GestureDetector(getActivity(), new SimpleOnGestureListener() {
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float vx, float vy) {
- Log.d(Constants.TAG, "x: " + vx + ", y: " + vy);
- if (vx < -2000) {
- decreaseLogLevel();
- } else if (vx > 2000) {
- increaseLogLevel();
- }
- return true;
- }
- });
+ getListView().setOnItemClickListener(this);
getListView().setFastScrollEnabled(true);
getListView().setDividerHeight(0);
- getListView().setOnTouchListener(this);
- }
-
- public void decreaseLogLevel() {
- switch (mLevel) {
- case DEBUG: mLevel = LogLevel.INFO; break;
- case INFO: mLevel = LogLevel.WARN; break;
- }
- refreshLevel();
- }
-
- public void increaseLogLevel() {
- switch (mLevel) {
- case INFO: mLevel = LogLevel.DEBUG; break;
- case WARN: mLevel = LogLevel.INFO; break;
- }
- refreshLevel();
- }
-
- private void refreshLevel() {
- /* TODO not sure if this is a good idea
- if (!mAdapters.containsKey(mLevel)) {
- mAdapters.put(mLevel, new LogAdapter(getActivity(), mResult.getLog(), mLevel));
- }
- mAdapter = mAdapters.get(mLevel);
- setListAdapter(mAdapter);
- */
}
@Override
- public boolean onTouch(View v, MotionEvent event) {
- mDetector.onTouchEvent(event);
- return false;
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ LogEntryParcel parcel = mAdapter.getItem(position);
+ if ( ! (parcel instanceof SubLogEntryParcel)) {
+ return;
+ }
+ Intent intent = new Intent(
+ getActivity(), LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ((SubLogEntryParcel) parcel).getSubResult());
+ startActivity(intent);
}
private class LogAdapter extends ArrayAdapter<LogEntryParcel> {
@@ -131,26 +89,24 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener
private LayoutInflater mInflater;
private int dipFactor;
- public LogAdapter(Context context, OperationResult.OperationLog log, LogLevel level) {
- super(context, R.layout.log_display_item);
+ public LogAdapter(Context context, OperationResult.OperationLog log) {
+ super(context, R.layout.log_display_item, log.toList());
mInflater = LayoutInflater.from(getContext());
dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
(float) 8, getResources().getDisplayMetrics());
- // we can't use addAll for a LogLevel.DEBUG shortcut here, unfortunately :(
- for (LogEntryParcel e : log) {
- if (e.mType.mLevel.ordinal() >= level.ordinal()) {
- add(e);
- }
- }
- notifyDataSetChanged();
}
private class ItemHolder {
- final TextView mText;
- final ImageView mImg;
- public ItemHolder(TextView text, ImageView image) {
+ final View mSecond;
+ final TextView mText, mSecondText;
+ final ImageView mImg, mSecondImg, mSub;
+ public ItemHolder(TextView text, ImageView image, ImageView sub, View second, TextView secondText, ImageView secondImg) {
mText = text;
mImg = image;
+ mSub = sub;
+ mSecond = second;
+ mSecondText = secondText;
+ mSecondImg = secondImg;
}
}
@@ -162,13 +118,55 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener
convertView = mInflater.inflate(R.layout.log_display_item, parent, false);
ih = new ItemHolder(
(TextView) convertView.findViewById(R.id.log_text),
- (ImageView) convertView.findViewById(R.id.log_img)
+ (ImageView) convertView.findViewById(R.id.log_img),
+ (ImageView) convertView.findViewById(R.id.log_sub),
+ convertView.findViewById(R.id.log_second),
+ (TextView) convertView.findViewById(R.id.log_second_text),
+ (ImageView) convertView.findViewById(R.id.log_second_img)
);
convertView.setTag(ih);
} else {
ih = (ItemHolder) convertView.getTag();
}
+ if (entry instanceof SubLogEntryParcel) {
+ ih.mSub.setVisibility(View.VISIBLE);
+ convertView.setClickable(false);
+
+ OperationResult result = ((SubLogEntryParcel) entry).getSubResult();
+ LogEntryParcel subEntry = result.getLog().getLast();
+ if (subEntry != null) {
+ ih.mSecond.setVisibility(View.VISIBLE);
+ // special case: first parameter may be a quantity
+ if (subEntry.mParameters != null && subEntry.mParameters.length > 0
+ && subEntry.mParameters[0] instanceof Integer) {
+ ih.mSecondText.setText(getResources().getQuantityString(subEntry.mType.getMsgId(),
+ (Integer) subEntry.mParameters[0],
+ subEntry.mParameters));
+ } else {
+ ih.mSecondText.setText(getResources().getString(subEntry.mType.getMsgId(),
+ subEntry.mParameters));
+ }
+ ih.mSecondText.setTextColor(subEntry.mType.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK);
+ switch (subEntry.mType.mLevel) {
+ case DEBUG: ih.mSecondImg.setBackgroundColor(Color.GRAY); break;
+ case INFO: ih.mSecondImg.setBackgroundColor(Color.BLACK); break;
+ case WARN: ih.mSecondImg.setBackgroundColor(Color.YELLOW); break;
+ case ERROR: ih.mSecondImg.setBackgroundColor(Color.RED); break;
+ case START: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.emphasis)); break;
+ case OK: ih.mSecondImg.setBackgroundColor(Color.GREEN); break;
+ case CANCELLED: ih.mSecondImg.setBackgroundColor(Color.RED); break;
+ }
+ } else {
+ ih.mSecond.setVisibility(View.GONE);
+ }
+
+ } else {
+ ih.mSub.setVisibility(View.GONE);
+ ih.mSecond.setVisibility(View.GONE);
+ convertView.setClickable(true);
+ }
+
// special case: first parameter may be a quantity
if (entry.mParameters != null && entry.mParameters.length > 0
&& entry.mParameters[0] instanceof Integer) {
@@ -179,14 +177,14 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener
ih.mText.setText(getResources().getString(entry.mType.getMsgId(),
entry.mParameters));
}
- ih.mText.setTextColor(entry.mType.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK);
convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0);
+ ih.mText.setTextColor(entry.mType.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK);
switch (entry.mType.mLevel) {
case DEBUG: ih.mImg.setBackgroundColor(Color.GRAY); break;
case INFO: ih.mImg.setBackgroundColor(Color.BLACK); break;
case WARN: ih.mImg.setBackgroundColor(Color.YELLOW); break;
case ERROR: ih.mImg.setBackgroundColor(Color.RED); break;
- case START: ih.mImg.setBackgroundColor(Color.GREEN); break;
+ case START: ih.mImg.setBackgroundColor(getResources().getColor(R.color.emphasis)); break;
case OK: ih.mImg.setBackgroundColor(Color.GREEN); break;
case CANCELLED: ih.mImg.setBackgroundColor(Color.RED); break;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyActivity.java
new file mode 100644
index 000000000..64853519d
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2011 Senecaso
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+
+import org.sufficientlysecure.keychain.R;
+
+/**
+ * Signs the specified public key with the specified secret master key
+ */
+public class MultiCertifyKeyActivity extends ActionBarActivity {
+
+ public static final String EXTRA_RESULT = "operation_result";
+ public static final String EXTRA_KEY_IDS = "extra_key_ids";
+ public static final String EXTRA_CERTIFY_KEY_ID = "certify_key_id";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.multi_certify_key_activity);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyFragment.java
new file mode 100644
index 000000000..ddfbac03c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyFragment.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.PorterDuff;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ListView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.service.results.CertifyResult;
+import org.sufficientlysecure.keychain.service.results.OperationResult;
+import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
+import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
+
+public class MultiCertifyKeyFragment extends LoaderFragment
+ implements LoaderManager.LoaderCallbacks<Cursor> {
+
+ private FragmentActivity mActivity;
+
+ private CheckBox mUploadKeyCheckbox;
+ ListView mUserIds;
+
+ private CertifyKeySpinner mCertifyKeySpinner;
+
+ private long[] mPubMasterKeyIds;
+
+ private long mSignMasterKeyId = Constants.key.none;
+
+ public static final String[] USER_IDS_PROJECTION = new String[]{
+ UserIds._ID,
+ UserIds.MASTER_KEY_ID,
+ UserIds.USER_ID,
+ UserIds.IS_PRIMARY,
+ UserIds.IS_REVOKED
+ };
+ private static final int INDEX_MASTER_KEY_ID = 1;
+ private static final int INDEX_USER_ID = 2;
+ private static final int INDEX_IS_PRIMARY = 3;
+ private static final int INDEX_IS_REVOKED = 4;
+
+ private MultiUserIdsAdapter mUserIdsAdapter;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ // Start out with a progress indicator.
+ setContentShown(false);
+
+ mPubMasterKeyIds = mActivity.getIntent().getLongArrayExtra(MultiCertifyKeyActivity.EXTRA_KEY_IDS);
+ if (mPubMasterKeyIds == null) {
+ Log.e(Constants.TAG, "List of key ids to certify missing!");
+ mActivity.finish();
+ return;
+ }
+
+ // preselect certify key id if given
+ long certifyKeyId = mActivity.getIntent().getLongExtra(MultiCertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none);
+ if (certifyKeyId != Constants.key.none) {
+ try {
+ CachedPublicKeyRing key = (new ProviderHelper(getActivity())).getCachedPublicKeyRing(certifyKeyId);
+ if (key.canCertify()) {
+ mCertifyKeySpinner.setSelectedKeyId(certifyKeyId);
+ }
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "certify certify check failed", e);
+ }
+ }
+
+ mUserIdsAdapter = new MultiUserIdsAdapter(mActivity, null, 0);
+ mUserIds.setAdapter(mUserIdsAdapter);
+ mUserIds.setDividerHeight(0);
+
+ getLoaderManager().initLoader(0, null, this);
+
+ OperationResult result = mActivity.getIntent().getParcelableExtra(MultiCertifyKeyActivity.EXTRA_RESULT);
+ if (result != null) {
+ // display result from import
+ result.createNotify(mActivity).show();
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+
+ // is this "the android way"?
+ mActivity = getActivity();
+
+ View view = inflater.inflate(R.layout.multi_certify_key_fragment, getContainer());
+
+ 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);
+
+ // make certify image gray, like action icons
+ ImageView vActionCertifyImage =
+ (ImageView) view.findViewById(R.id.certify_key_action_certify_image);
+ vActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
+ PorterDuff.Mode.SRC_IN);
+
+ mCertifyKeySpinner.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() {
+ @Override
+ public void onKeyChanged(long masterKeyId) {
+ mSignMasterKeyId = masterKeyId;
+ }
+ });
+
+ View vCertifyButton = view.findViewById(R.id.certify_key_certify_button);
+ vCertifyButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (mSignMasterKeyId == Constants.key.none) {
+ Notify.showNotify(mActivity, getString(R.string.select_key_to_certify),
+ Notify.Style.ERROR);
+ } else {
+ initiateCertifying();
+ }
+ }
+ });
+
+ return root;
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ Uri uri = UserIds.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 = UserIds.IS_REVOKED + " = 0" + " AND "
+ + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
+ + " IN (" + placeholders + ")";
+ }
+
+ return new CursorLoader(mActivity, uri,
+ USER_IDS_PROJECTION, selection, ids,
+ Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " ASC"
+ + ", " + Tables.USER_IDS + "." + UserIds.USER_ID + " ASC"
+ );
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+
+ MatrixCursor matrix = new MatrixCursor(new String[]{
+ "_id", "user_data", "grouped"
+ });
+ data.moveToFirst();
+
+ long lastMasterKeyId = 0;
+ String lastName = "";
+ ArrayList<String> uids = new ArrayList<String>();
+
+ 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);
+ String[] pieces = KeyRing.splitUserId(userId);
+
+ // Two cases:
+
+ boolean grouped = masterKeyId == lastMasterKeyId;
+ boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces[0]);
+ // Remember for next loop
+ lastName = pieces[0];
+
+ 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);
+ setContentShown(true, isResumed());
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mUserIdsAdapter.swapCursor(null);
+ }
+
+ /**
+ * handles the UI bits of the signing process on the UI thread
+ */
+ private void initiateCertifying() {
+ // get the user's passphrase for this key (if required)
+ String passphrase;
+ try {
+ passphrase = PassphraseCacheService.getCachedPassphrase(mActivity, mSignMasterKeyId, mSignMasterKeyId);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ Log.e(Constants.TAG, "Key not found!", e);
+ mActivity.finish();
+ return;
+ }
+ if (passphrase == null) {
+ PassphraseDialogFragment.show(mActivity, mSignMasterKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ startCertifying();
+ }
+ }
+ }
+ );
+ // bail out; need to wait until the user has entered the passphrase before trying again
+ } else {
+ startCertifying();
+ }
+ }
+
+ /**
+ * kicks off the actual signing process on a background thread
+ */
+ private void startCertifying() {
+ // Bail out if there is not at least one user id selected
+ ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
+ if (certifyActions.isEmpty()) {
+ Notify.showNotify(mActivity, "No identities selected!",
+ Notify.Style.ERROR);
+ return;
+ }
+
+ // Send all information needed to service to sign key in other thread
+ Intent intent = new Intent(mActivity, KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING);
+
+ // fill values for this action
+ CertifyActionsParcel parcel = new CertifyActionsParcel(mSignMasterKeyId);
+ parcel.mCertifyActions.addAll(certifyActions);
+
+ Bundle data = new Bundle();
+ data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel);
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after signing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity,
+ getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+
+ Bundle data = message.getData();
+ CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
+
+ Intent intent = new Intent();
+ intent.putExtra(CertifyResult.EXTRA_RESULT, result);
+ mActivity.setResult(CertifyKeyActivity.RESULT_OK, intent);
+
+ // check if we need to send the key to the server or not
+ if (mUploadKeyCheckbox.isChecked()) {
+ // upload the newly signed key to the keyserver
+ // TODO implement
+ // uploadKey();
+ } else {
+ mActivity.finish();
+ }
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(mActivity);
+
+ // start service with intent
+ mActivity.startService(intent);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java
index 6193be297..cc66a33a3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java
@@ -17,10 +17,12 @@
package org.sufficientlysecure.keychain.ui;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
import org.sufficientlysecure.keychain.Constants;
@@ -38,8 +40,6 @@ public class QrCodeActivity extends ActionBarActivity {
private ImageView mFingerprintQrCode;
- private static final int QR_CODE_SIZE = 1000;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -87,7 +87,20 @@ public class QrCodeActivity extends ActionBarActivity {
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
- mFingerprintQrCode.setImageBitmap(QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE));
+
+ // create a minimal size qr code, we can keep this in ram no problem
+ final Bitmap qrCode = QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0);
+
+ mFingerprintQrCode.getViewTreeObserver().addOnGlobalLayoutListener(
+ new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ // create actual bitmap in display dimensions
+ Bitmap scaled = Bitmap.createScaledBitmap(qrCode,
+ mFingerprintQrCode.getWidth(), mFingerprintQrCode.getWidth(), false);
+ mFingerprintQrCode.setImageBitmap(scaled);
+ }
+ });
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
index 9343b166a..621b6b7e1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
@@ -42,9 +42,11 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.util.Vector;
@@ -257,9 +259,10 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
KeyRings.USER_ID,
- KeyRings.EXPIRY,
+ KeyRings.IS_EXPIRED,
KeyRings.IS_REVOKED,
KeyRings.HAS_ENCRYPT,
+ KeyRings.VERIFIED,
};
String inMasterKeyList = null;
@@ -344,7 +347,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
private class SelectPublicKeyCursorAdapter extends SelectKeyCursorAdapter {
- private int mIndexHasEncrypt;
+ private int mIndexHasEncrypt, mIndexIsVerified;
public SelectPublicKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView) {
super(context, c, flags, listView);
@@ -355,6 +358,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
super.initIndex(cursor);
if (cursor != null) {
mIndexHasEncrypt = cursor.getColumnIndexOrThrow(KeyRings.HAS_ENCRYPT);
+ mIndexIsVerified = cursor.getColumnIndexOrThrow(KeyRings.VERIFIED);
}
}
@@ -369,12 +373,18 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
h.selected.setChecked(getListView().isItemChecked(cursor.getPosition()));
boolean enabled = false;
- if((Boolean) h.status.getTag()) {
+ if((Boolean) h.statusIcon.getTag()) {
// Check if key is viable for our purposes
if (cursor.getInt(mIndexHasEncrypt) == 0) {
- h.status.setText(R.string.no_key);
+ h.statusIcon.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, KeyFormattingUtils.STATE_UNAVAILABLE);
+ enabled = false;
+ } else if (cursor.getInt(mIndexIsVerified) != 0) {
+ h.statusIcon.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, KeyFormattingUtils.STATE_VERIFIED);
+ enabled = true;
} else {
- h.status.setText(R.string.can_encrypt);
+ h.statusIcon.setVisibility(View.GONE);
enabled = true;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
index 0f9a52be6..1efd2c935 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
@@ -21,11 +21,6 @@ import android.annotation.TargetApi;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -37,6 +32,7 @@ import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.AlphaAnimation;
import android.widget.ImageView;
import android.widget.TextView;
@@ -404,21 +400,25 @@ public class ViewKeyShareFragment extends LoaderFragment implements
new AsyncTask<Void, Void, Bitmap>() {
protected Bitmap doInBackground(Void... unused) {
String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
- return QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE);
+ // render with minimal size
+ return QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0);
}
protected void onPostExecute(Bitmap qrCode) {
// only change view, if fragment is attached to activity
if (ViewKeyShareFragment.this.isAdded()) {
- // Transition drawable with a transparent drawable and the final bitmap
- final TransitionDrawable td =
- new TransitionDrawable(new Drawable[]{
- new ColorDrawable(Color.TRANSPARENT),
- new BitmapDrawable(getResources(), qrCode)
- });
-
- mFingerprintQrCode.setImageDrawable(td);
- td.startTransition(200);
+
+ // scale the image up to our actual size. we do this in code rather
+ // than let the ImageView do this because we don't require filtering.
+ Bitmap scaled = Bitmap.createScaledBitmap(qrCode,
+ mFingerprintQrCode.getHeight(), mFingerprintQrCode.getHeight(),
+ false);
+ mFingerprintQrCode.setImageBitmap(scaled);
+
+ // simple fade-in animation
+ AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
+ anim.setDuration(200);
+ mFingerprintQrCode.startAnimation(anim);
}
}
};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java
index 5f2aec4fe..dc0c5846a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java
@@ -17,6 +17,8 @@
package org.sufficientlysecure.keychain.ui.adapter;
+import org.sufficientlysecure.keychain.service.results.OperationResult;
+
/**
* The AsyncTaskResultWrapper is used to wrap a result from a AsyncTask (for example: Loader).
* You can pass the result and an exception in it if an error occurred.
@@ -28,19 +30,19 @@ package org.sufficientlysecure.keychain.ui.adapter;
public class AsyncTaskResultWrapper<T> {
private final T mResult;
- private final Exception mError;
+ private final OperationResult mOperationResult;
- public AsyncTaskResultWrapper(T result, Exception error) {
+ public AsyncTaskResultWrapper(T result, OperationResult operationResult) {
this.mResult = result;
- this.mError = error;
+ this.mOperationResult = operationResult;
}
public T getResult() {
return mResult;
}
- public Exception getError() {
- return mError;
+ public OperationResult getOperationResult() {
+ return mOperationResult;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java
index 0332e8882..cb51a15a9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java
@@ -21,6 +21,9 @@ import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.keyimport.Keyserver;
+import org.sufficientlysecure.keychain.service.results.GetKeyResult;
+import org.sufficientlysecure.keychain.service.results.OperationResult;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.keyimport.CloudSearch;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
@@ -93,7 +96,8 @@ public class ImportKeysListCloudLoader
*/
private void queryServer(boolean enforceFingerprint) {
try {
- ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(mServerQuery, mCloudPrefs);
+ ArrayList<ImportKeysListEntry> searchResult
+ = CloudSearch.search(mServerQuery, mCloudPrefs);
mEntryList.clear();
// add result to data
@@ -114,9 +118,29 @@ public class ImportKeysListCloudLoader
} else {
mEntryList.addAll(searchResult);
}
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
- } catch (Exception e) {
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
+ GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, getKeyResult);
+ } catch (Keyserver.CloudSearchFailureException e) {
+ // convert exception to result parcel
+ int error = GetKeyResult.RESULT_ERROR;
+ OperationResult.LogType logType = null;
+ if (e instanceof Keyserver.QueryFailedException) {
+ error = GetKeyResult.RESULT_ERROR_QUERY_FAILED;
+ logType = OperationResult.LogType.MSG_GET_QUERY_FAILED;
+ } else if (e instanceof Keyserver.TooManyResponsesException) {
+ error = GetKeyResult.RESULT_ERROR_TOO_MANY_RESPONSES;
+ logType = OperationResult.LogType.MSG_GET_TOO_MANY_RESPONSES;
+ } else if (e instanceof Keyserver.QueryTooShortException) {
+ error = GetKeyResult.RESULT_ERROR_QUERY_TOO_SHORT;
+ logType = OperationResult.LogType.MSG_GET_QUERY_TOO_SHORT;
+ } else if (e instanceof Keyserver.QueryTooShortOrTooManyResponsesException) {
+ error = GetKeyResult.RESULT_ERROR_TOO_SHORT_OR_TOO_MANY_RESPONSES;
+ logType = OperationResult.LogType.MSG_GET_QUERY_TOO_SHORT_OR_TOO_MANY_RESPONSES;
+ }
+ OperationResult.OperationLog log = new OperationResult.OperationLog();
+ log.add(logType, 0);
+ GetKeyResult getKeyResult = new GetKeyResult(error, log);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, getKeyResult);
}
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
index 04947da93..6664cb61a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -25,6 +25,8 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.service.results.GetKeyResult;
+import org.sufficientlysecure.keychain.service.results.OperationResult;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
@@ -37,9 +39,6 @@ import java.util.Iterator;
public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
- public static class NoValidKeysException extends Exception {
- }
-
public static class NonPgpPartException extends Exception {
private int mCount;
@@ -72,7 +71,8 @@ public class ImportKeysListLoader
return mEntryListWrapper;
}
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, null);
+ GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, getKeyResult);
if (mInputData == null) {
Log.e(Constants.TAG, "Input data is null!");
@@ -136,13 +136,11 @@ public class ImportKeysListLoader
}
} catch (IOException e) {
Log.e(Constants.TAG, "IOException on parsing key file! Return NoValidKeysException!", e);
-
- NoValidKeysException e1 = new NoValidKeysException();
+ OperationResult.OperationLog log = new OperationResult.OperationLog();
+ log.add(OperationResult.LogType.MSG_GET_NO_VALID_KEYS, 0);
+ GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_ERROR_NO_VALID_KEYS, log);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
- (mData, e1);
- } catch (Exception e) {
- Log.e(Constants.TAG, "Other Exception on parsing key file!", e);
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
+ (mData, getKeyResult);
}
}
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
new file mode 100644
index 000000000..2c0bf0842
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Parcel;
+import android.support.v4.util.LongSparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.support.v4.widget.CursorAdapter;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
+
+public class MultiUserIdsAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+ private final ArrayList<Boolean> mCheckStates;
+
+ public MultiUserIdsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+ mInflater = LayoutInflater.from(context);
+ mCheckStates = new ArrayList<Boolean>();
+ }
+
+ @Override
+ public Cursor swapCursor(Cursor newCursor) {
+ mCheckStates.clear();
+ if (newCursor != null) {
+ int count = newCursor.getCount();
+ mCheckStates.ensureCapacity(count);
+ // initialize to true (use case knowledge: we usually want to sign all uids)
+ for (int i = 0; i < count; i++) {
+ mCheckStates.add(true);
+ }
+ }
+
+ return super.swapCursor(newCursor);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.multi_certify_item, null);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ View vHeader = view.findViewById(R.id.user_id_header);
+ TextView vHeaderId = (TextView) view.findViewById(R.id.user_id_header_id);
+ TextView vName = (TextView) view.findViewById(R.id.user_id_item_name);
+ TextView vAddresses = (TextView) view.findViewById(R.id.user_id_item_addresses);
+
+ byte[] data = cursor.getBlob(1);
+ int isHeader = cursor.getInt(2);
+ Parcel p = Parcel.obtain();
+ p.unmarshall(data, 0, data.length);
+ p.setDataPosition(0);
+ ArrayList<String> uids = p.createStringArrayList();
+ p.recycle();
+
+ if (isHeader == 1) {
+ long masterKeyId = cursor.getLong(0);
+ vHeader.setVisibility(View.VISIBLE);
+ vHeaderId.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(mContext, masterKeyId));
+ } else {
+ vHeader.setVisibility(View.GONE);
+ }
+
+ { // first one
+ String userId = uids.get(0);
+ String[] splitUserId = KeyRing.splitUserId(userId);
+ if (splitUserId[0] != null) {
+ vName.setText(splitUserId[0]);
+ } else {
+ vName.setText(R.string.user_id_no_name);
+ }
+ }
+
+ StringBuilder lines = new StringBuilder();
+ for (String uid : uids) {
+ String[] splitUserId = KeyRing.splitUserId(uid);
+ if (splitUserId[1] == null) {
+ continue;
+ }
+ lines.append(splitUserId[1]);
+ if (splitUserId[2] != null) {
+ lines.append(" (").append(splitUserId[2]).append(")");
+ }
+ lines.append('\n');
+ }
+
+ // If we have any data here, show it
+ if (lines.length() > 0) {
+ // delete last newline
+ lines.setLength(lines.length() - 1);
+ vAddresses.setVisibility(View.VISIBLE);
+ vAddresses.setText(lines);
+ } else {
+ vAddresses.setVisibility(View.GONE);
+ }
+
+ final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.user_id_item_check_box);
+ final int position = cursor.getPosition();
+ vCheckBox.setOnCheckedChangeListener(null);
+ vCheckBox.setChecked(mCheckStates.get(position));
+ vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ mCheckStates.set(position, b);
+ }
+ });
+ vCheckBox.setClickable(false);
+
+ View vUidBody = view.findViewById(R.id.user_id_body);
+ vUidBody.setClickable(true);
+ vUidBody.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ vCheckBox.toggle();
+ }
+ });
+
+ }
+
+ public ArrayList<CertifyAction> getSelectedCertifyActions() {
+ LongSparseArray<CertifyAction> actions = new LongSparseArray<CertifyAction>();
+ for (int i = 0; i < mCheckStates.size(); i++) {
+ if (mCheckStates.get(i)) {
+ mCursor.moveToPosition(i);
+
+ long keyId = mCursor.getLong(0);
+ byte[] data = mCursor.getBlob(1);
+
+ Parcel p = Parcel.obtain();
+ p.unmarshall(data, 0, data.length);
+ p.setDataPosition(0);
+ ArrayList<String> uids = p.createStringArrayList();
+ p.recycle();
+
+ CertifyAction action = actions.get(keyId);
+ if (actions.get(keyId) == null) {
+ actions.put(keyId, new CertifyAction(keyId, uids));
+ } else {
+ action.mUserIds.addAll(uids);
+ }
+ }
+ }
+
+ ArrayList<CertifyAction> result = new ArrayList<CertifyAction>(actions.size());
+ for (int i = 0; i < actions.size(); i++) {
+ result.add(actions.valueAt(i));
+ }
+ return result;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
index 6947fc1ff..c864c7138 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
@@ -24,6 +24,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
+import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
@@ -33,8 +34,6 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.util.Highlighter;
-import java.util.Date;
-
/**
* Yes this class is abstract!
@@ -44,7 +43,7 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
private String mQuery;
private LayoutInflater mInflater;
- protected int mIndexUserId, mIndexMasterKeyId, mIndexRevoked, mIndexExpiry;
+ protected int mIndexUserId, mIndexMasterKeyId, mIndexIsExpiry, mIndexIsRevoked;
public SelectKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView) {
super(context, c, flags);
@@ -73,8 +72,8 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
if (cursor != null) {
mIndexUserId = cursor.getColumnIndexOrThrow(KeyRings.USER_ID);
mIndexMasterKeyId = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID);
- mIndexExpiry = cursor.getColumnIndexOrThrow(KeyRings.EXPIRY);
- mIndexRevoked = cursor.getColumnIndexOrThrow(KeyRings.IS_REVOKED);
+ mIndexIsExpiry = cursor.getColumnIndexOrThrow(KeyRings.IS_EXPIRED);
+ mIndexIsRevoked = cursor.getColumnIndexOrThrow(KeyRings.IS_REVOKED);
}
}
@@ -90,7 +89,8 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
public static class ViewHolderItem {
public View view;
- public TextView mainUserId, mainUserIdRest, keyId, status;
+ public TextView mainUserId, mainUserIdRest, keyId;
+ public ImageView statusIcon;
public CheckBox selected;
public void setEnabled(boolean enabled) {
@@ -99,7 +99,7 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
mainUserId.setEnabled(enabled);
mainUserIdRest.setEnabled(enabled);
keyId.setEnabled(enabled);
- status.setEnabled(enabled);
+ statusIcon.setEnabled(enabled);
// Sorta special: We set an item as clickable to disable it in the ListView. This works
// because the list item will handle the clicks itself (which is a nop)
@@ -130,19 +130,21 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
long masterKeyId = cursor.getLong(mIndexMasterKeyId);
h.keyId.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(mContext, masterKeyId));
- boolean enabled = true;
- if (cursor.getInt(mIndexRevoked) != 0) {
- h.status.setText(R.string.revoked);
+ boolean enabled;
+ if (cursor.getInt(mIndexIsRevoked) != 0) {
+ h.statusIcon.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, KeyFormattingUtils.STATE_REVOKED);
enabled = false;
- } else if (!cursor.isNull(mIndexExpiry)
- && new Date(cursor.getLong(mIndexExpiry) * 1000).before(new Date())) {
- h.status.setText(R.string.expired);
+ } else if (cursor.getInt(mIndexIsExpiry) != 0) {
+ h.statusIcon.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, KeyFormattingUtils.STATE_EXPIRED);
enabled = false;
} else {
- h.status.setText("");
+ h.statusIcon.setVisibility(View.GONE);
+ enabled = true;
}
- h.status.setTag(enabled);
+ h.statusIcon.setTag(enabled);
}
@Override
@@ -153,7 +155,7 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
holder.mainUserId = (TextView) view.findViewById(R.id.mainUserId);
holder.mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
holder.keyId = (TextView) view.findViewById(R.id.subkey_item_key_id);
- holder.status = (TextView) view.findViewById(R.id.status);
+ holder.statusIcon = (ImageView) view.findViewById(R.id.status_icon);
holder.selected = (CheckBox) view.findViewById(R.id.selected);
view.setTag(holder);
return view;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/UserIdInfoDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/UserIdInfoDialogFragment.java
index 968b2429b..57b171eb9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/UserIdInfoDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/UserIdInfoDialogFragment.java
@@ -64,12 +64,12 @@ public class UserIdInfoDialogFragment extends DialogFragment {
} else {
switch (isVerified) {
case KeychainContract.Certs.VERIFIED_SECRET:
- title = getString(R.string.user_id_info_verified_title);
- message = getString(R.string.user_id_info_verified_text);
+ title = getString(R.string.user_id_info_certified_title);
+ message = getString(R.string.user_id_info_certified_text);
break;
case KeychainContract.Certs.VERIFIED_SELF:
- title = getString(R.string.user_id_info_not_verified_title);
- message = getString(R.string.user_id_info_not_verified_text);
+ title = getString(R.string.user_id_info_uncertified_title);
+ message = getString(R.string.user_id_info_uncertified_text);
break;
default:
title = getString(R.string.user_id_info_invalid_title);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
index 5c2bc76d0..afc3247be 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
@@ -19,18 +19,13 @@
package org.sufficientlysecure.keychain.ui.util;
import android.content.Context;
-import android.database.Cursor;
import android.graphics.Color;
import android.graphics.PorterDuff;
-import android.graphics.Typeface;
import android.text.Spannable;
-import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
-import android.text.style.StyleSpan;
-import android.text.style.TypefaceSpan;
-import android.view.View;
import android.widget.ImageView;
+import android.widget.TextView;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.nist.NISTNamedCurves;
@@ -291,7 +286,7 @@ public class KeyFormattingUtils {
}
public static String beautifyKeyIdWithPrefix(Context context, String idHex) {
- return "ID: " + beautifyKeyId(idHex);
+ return "Key ID: " + beautifyKeyId(idHex);
}
public static String beautifyKeyIdWithPrefix(Context context, long keyId) {
@@ -386,36 +381,126 @@ public class KeyFormattingUtils {
public static final int STATE_EXPIRED = 2;
public static final int STATE_VERIFIED = 3;
public static final int STATE_UNAVAILABLE = 4;
+ public static final int STATE_ENCRYPTED = 5;
+ public static final int STATE_NOT_ENCRYPTED = 6;
+ public static final int STATE_UNVERIFIED = 7;
+ public static final int STATE_UNKNOWN_KEY = 8;
+ public static final int STATE_INVALID = 9;
+ public static final int STATE_NOT_SIGNED = 10;
+
+ public static void setStatusImage(Context context, ImageView statusIcon, int state) {
+ setStatusImage(context, statusIcon, null, state);
+ }
/**
* Sets status image based on constant
*/
- public static void setStatusImage(Context context, ImageView statusView, int state) {
+ public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText, int state) {
switch (state) {
- case STATE_REVOKED:
- statusView.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout));
- statusView.setColorFilter(context.getResources().getColor(R.color.android_red_dark),
+ /** GREEN: everything is good **/
+ case STATE_VERIFIED: {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_verified_cutout));
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.android_green_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.android_green_dark));
+ }
+ break;
+ }
+ case STATE_ENCRYPTED: {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_lock_closed));
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.android_green_dark),
PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.android_green_dark));
+ }
break;
- case STATE_EXPIRED:
- statusView.setImageDrawable(
+ }
+ /** ORANGE: mostly bad... **/
+ case STATE_UNVERIFIED: {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout));
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.android_orange_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.android_orange_dark));
+ }
+ break;
+ }
+ case STATE_EXPIRED: {
+ statusIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_expired_cutout));
- statusView.setColorFilter(context.getResources().getColor(R.color.android_orange_dark),
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.android_orange_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.android_orange_dark));
+ }
+ break;
+ }
+ case STATE_UNKNOWN_KEY: {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout));
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.android_orange_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.android_orange_dark));
+ }
+ break;
+ }
+ /** RED: really bad... **/
+ case STATE_REVOKED: {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout));
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.android_red_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.android_red_dark));
+ }
+ break;
+ }
+ case STATE_NOT_ENCRYPTED: {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_lock_open));
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.android_red_dark),
PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.android_red_dark));
+ }
break;
- case STATE_UNAVAILABLE:
- statusView.setImageDrawable(
+ }
+ case STATE_NOT_SIGNED: {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout));
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.android_red_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.android_red_dark));
+ }
+ break;
+ }
+ case STATE_INVALID: {
+ statusIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout));
- statusView.setColorFilter(context.getResources().getColor(R.color.bg_gray),
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.android_red_dark),
PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.android_red_dark));
+ }
break;
- case STATE_VERIFIED:
- statusView.setImageDrawable(
- context.getResources().getDrawable(R.drawable.status_signature_verified_cutout));
- statusView.setColorFilter(context.getResources().getColor(R.color.android_green_dark),
+ }
+ /** special **/
+ case STATE_UNAVAILABLE: {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout));
+ statusIcon.setColorFilter(context.getResources().getColor(R.color.bg_gray),
PorterDuff.Mode.SRC_ATOP);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(R.color.bg_gray));
+ }
break;
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java
index dd07a16b0..36f38045f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java
@@ -37,7 +37,6 @@ import java.util.Hashtable;
* Copied from Bitcoin Wallet
*/
public class QrCodeUtils {
- public static final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
/**
* Generate Bitmap with QR Code based on input.
@@ -50,7 +49,7 @@ public class QrCodeUtils {
try {
final Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
- final BitMatrix result = QR_CODE_WRITER.encode(input, BarcodeFormat.QR_CODE, size,
+ final BitMatrix result = new QRCodeWriter().encode(input, BarcodeFormat.QR_CODE, size,
size, hints);
final int width = result.getWidth();
@@ -60,7 +59,7 @@ public class QrCodeUtils {
for (int y = 0; y < height; y++) {
final int offset = y * width;
for (int x = 0; x < width; x++) {
- pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT;
+ pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
}
}
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 07f6529f1..0a1a1d75b 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
@@ -84,13 +84,19 @@ public class CertifyKeySpinner extends KeySpinner {
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
super.onLoadFinished(loader, data);
- // If there is only one choice, pick it by default
- if (mAdapter.getCount() == 2) {
- setSelection(1);
+
+ if (loader.getId() == LOADER_ID) {
+ // If there is only one choice, pick it by default
+ if (mAdapter.getCount() == 2) {
+ // preselect if key can certify
+ if (data.moveToPosition(1) && data.isNull(mIndexHasCertify)) {
+ setSelection(1);
+ }
+ }
+ mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY);
+ mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
+ mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
}
- mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY);
- mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
- mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ExchangeKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ExchangeKeySpinner.java
new file mode 100644
index 000000000..e31d14d48
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ExchangeKeySpinner.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.widget;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+
+public class ExchangeKeySpinner extends KeySpinner {
+ public ExchangeKeySpinner(Context context) {
+ super(context);
+ }
+
+ public ExchangeKeySpinner(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ExchangeKeySpinner(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int loaderId, Bundle data) {
+ // This is called when a new Loader needs to be created. This
+ // sample only has one Loader, so we don't care about the ID.
+ Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri();
+
+ // These are the rows that we will retrieve.
+ String[] projection = new String[]{
+ KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.KEY_ID,
+ KeychainContract.KeyRings.USER_ID,
+ KeychainContract.KeyRings.IS_REVOKED,
+ KeychainContract.KeyRings.IS_EXPIRED,
+ KeychainContract.KeyRings.HAS_ANY_SECRET
+ };
+
+ String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1";
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(getContext(), baseUri, projection, where, null, null);
+ }
+
+ private int mIndexIsRevoked, mIndexIsExpired;
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ super.onLoadFinished(loader, data);
+
+ if (loader.getId() == LOADER_ID) {
+ // If there is only one choice, pick it by default
+ if (mAdapter.getCount() == 2) {
+ setSelection(1);
+ }
+ mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
+ mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
+ }
+ }
+
+ @Override
+ boolean setStatus(Context context, Cursor cursor, ImageView statusView) {
+ if (cursor.getInt(mIndexIsRevoked) != 0) {
+ KeyFormattingUtils.setStatusImage(getContext(), statusView, KeyFormattingUtils.STATE_REVOKED);
+ return false;
+ }
+ if (cursor.getInt(mIndexIsExpired) != 0) {
+ KeyFormattingUtils.setStatusImage(getContext(), statusView, KeyFormattingUtils.STATE_EXPIRED);
+ return false;
+ }
+
+ // valid key
+ return true;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
index bb4ae3ab6..7ec6ffe95 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
@@ -50,6 +50,9 @@ public abstract class KeySpinner extends Spinner implements LoaderManager.Loader
protected SelectKeyAdapter mAdapter = new SelectKeyAdapter();
protected OnKeyChangedListener mListener;
+ // this shall note collide with other loaders inside the activity
+ protected int LOADER_ID = 2343;
+
public KeySpinner(Context context) {
super(context);
initView();
@@ -101,7 +104,7 @@ public abstract class KeySpinner extends Spinner implements LoaderManager.Loader
public void reload() {
if (getContext() instanceof FragmentActivity) {
- ((FragmentActivity) getContext()).getSupportLoaderManager().restartLoader(0, null, this);
+ ((FragmentActivity) getContext()).getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
} else {
Log.e(Constants.TAG, "KeySpinner must be attached to FragmentActivity, this is " + getContext().getClass());
}
@@ -109,12 +112,16 @@ public abstract class KeySpinner extends Spinner implements LoaderManager.Loader
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- mAdapter.swapCursor(data);
+ if (loader.getId() == LOADER_ID) {
+ mAdapter.swapCursor(data);
+ }
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
- mAdapter.swapCursor(null);
+ if (loader.getId() == LOADER_ID) {
+ mAdapter.swapCursor(null);
+ }
}
public long getSelectedKeyId() {
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 ce1f7bb44..2f002d470 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
@@ -73,9 +73,12 @@ public class SignKeySpinner extends KeySpinner {
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
super.onLoadFinished(loader, data);
- mIndexHasSign = data.getColumnIndex(KeychainContract.KeyRings.HAS_SIGN);
- mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
- mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
+
+ if (loader.getId() == LOADER_ID) {
+ mIndexHasSign = data.getColumnIndex(KeychainContract.KeyRings.HAS_SIGN);
+ mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
+ mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
+ }
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java
index 357a9603c..afa2e75fa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java
@@ -59,7 +59,6 @@ public class KeyUpdateHelper {
for (String fprint : providerHelper.getAllFingerprints(KeychainContract.KeyRings.buildUnifiedKeyRingsUri())) {
ImportKeysListEntry key = new ImportKeysListEntry();
key.setFingerprintHex(fprint);
- key.setBitStrength(1337);
key.addOrigin(servers[0]);
keys.add(key);
}
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/encrypted_small.png b/OpenKeychain/src/main/res/drawable-hdpi/encrypted_small.png
deleted file mode 100644
index 3ff8e9b97..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/encrypted_small.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_action_cloud.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_cloud.png
new file mode 100644
index 000000000..3daa64131
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_cloud.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_action_search_cloud.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_search_cloud.png
new file mode 100644
index 000000000..ba7236da3
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_search_cloud.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/overlay_error.png b/OpenKeychain/src/main/res/drawable-hdpi/overlay_error.png
deleted file mode 100644
index e6d7e60ba..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/overlay_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/overlay_ok.png b/OpenKeychain/src/main/res/drawable-hdpi/overlay_ok.png
deleted file mode 100644
index 0672f869d..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/overlay_ok.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/revoked_key_small.png b/OpenKeychain/src/main/res/drawable-hdpi/revoked_key_small.png
deleted file mode 100644
index 75f45eb54..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/revoked_key_small.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/signed_large.png b/OpenKeychain/src/main/res/drawable-hdpi/signed_large.png
deleted file mode 100644
index c209f4167..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/signed_large.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/overlay_error.png b/OpenKeychain/src/main/res/drawable-ldpi/overlay_error.png
deleted file mode 100644
index e5a88e18f..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/overlay_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/overlay_ok.png b/OpenKeychain/src/main/res/drawable-ldpi/overlay_ok.png
deleted file mode 100644
index 63374d47f..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/overlay_ok.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/signed_large.png b/OpenKeychain/src/main/res/drawable-ldpi/signed_large.png
deleted file mode 100644
index d2917644c..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/signed_large.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_action_cloud.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_cloud.png
new file mode 100644
index 000000000..266d4c21f
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_cloud.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_action_search_cloud.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_search_cloud.png
new file mode 100644
index 000000000..e1067f73c
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_search_cloud.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/overlay_error.png b/OpenKeychain/src/main/res/drawable-mdpi/overlay_error.png
deleted file mode 100644
index 5fe017433..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/overlay_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/overlay_ok.png b/OpenKeychain/src/main/res/drawable-mdpi/overlay_ok.png
deleted file mode 100644
index b4f332260..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/overlay_ok.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/signed_large.png b/OpenKeychain/src/main/res/drawable-mdpi/signed_large.png
deleted file mode 100644
index ab9495e7b..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/signed_large.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/signed_small.png b/OpenKeychain/src/main/res/drawable-mdpi/signed_small.png
deleted file mode 100644
index 4202c3f97..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/signed_small.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_cloud.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_cloud.png
new file mode 100644
index 000000000..0769899fd
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_cloud.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_search_cloud.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_search_cloud.png
new file mode 100644
index 000000000..b81772f20
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_search_cloud.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_cloud.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_cloud.png
new file mode 100644
index 000000000..f97084dbe
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_cloud.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_search_cloud.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_search_cloud.png
new file mode 100644
index 000000000..7c8b36bc0
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_search_cloud.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable/overlay_error.png b/OpenKeychain/src/main/res/drawable/overlay_error.png
deleted file mode 100644
index 2372de59e..000000000
--- a/OpenKeychain/src/main/res/drawable/overlay_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable/overlay_ok.png b/OpenKeychain/src/main/res/drawable/overlay_ok.png
deleted file mode 100644
index 2f0005898..000000000
--- a/OpenKeychain/src/main/res/drawable/overlay_ok.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable/signed_large.png b/OpenKeychain/src/main/res/drawable/signed_large.png
deleted file mode 100644
index 92e64dc51..000000000
--- a/OpenKeychain/src/main/res/drawable/signed_large.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/layout/add_keys_activity.xml b/OpenKeychain/src/main/res/layout/add_keys_activity.xml
new file mode 100644
index 000000000..c703efbf3
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/add_keys_activity.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/content_frame"
+ android:layout_marginLeft="@dimen/drawer_content_padding"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include layout="@layout/notify_area" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="4dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:text="@string/add_keys_section_secure_exchange" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:paddingLeft="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/add_keys_my_key"
+ android:paddingRight="8dp" />
+
+ <org.sufficientlysecure.keychain.ui.widget.ExchangeKeySpinner
+ android:id="@+id/add_keys_safeslinger_key_spinner"
+ android:minHeight="56dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/add_keys_safeslinger"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ android:paddingRight="4dp"
+ style="@style/SelectableItem"
+ android:orientation="horizontal">
+
+ <TextView
+ android:paddingLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:text="@string/add_keys_start_exchange"
+ android:layout_weight="1"
+ android:gravity="center_vertical" />
+
+ <ImageView
+ android:id="@+id/add_keys_safeslinger_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:src="@drawable/ic_action_safeslinger"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
+
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="8dp" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="32dp"
+ android:text="@string/add_keys_section_secure_add" />
+
+ <TextView
+ android:id="@+id/add_keys_qr_code"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:text="@string/add_keys_qr_code"
+ android:drawableRight="@drawable/ic_action_qr_code"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical" />
+
+ <View
+ android:id="@+id/view_key_action_certify_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/add_keys_nfc"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:text="@string/add_keys_nfc"
+ android:drawableRight="@drawable/ic_action_nfc"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="8dp" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</ScrollView> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml
index 449c9fdf0..d92988111 100644
--- a/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml
@@ -135,7 +135,7 @@
android:layout_weight="1"
android:text="@string/btn_next"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:drawableRight="@drawable/ic_action_new_account"
+ android:drawableRight="@drawable/ic_action_play"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:clickable="true"
diff --git a/OpenKeychain/src/main/res/layout/decrypt_result_include.xml b/OpenKeychain/src/main/res/layout/decrypt_result_include.xml
index fcad91df3..8a0519872 100644
--- a/OpenKeychain/src/main/res/layout/decrypt_result_include.xml
+++ b/OpenKeychain/src/main/res/layout/decrypt_result_include.xml
@@ -1,97 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/result"
+ android:id="@+id/result_main_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:background="@color/android_purple_light">
+ android:background="@color/holo_gray_bright">
- <View
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/result_main_layout"
+ android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"
- android:layout_marginTop="4dp" />
-
- <TextView
- android:id="@+id/result_text"
- android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="result text"
- android:textColor="@color/white"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="8dp" />
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider" />
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
- <RelativeLayout
- android:id="@+id/result_signature"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clickable="true"
- android:orientation="horizontal"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="8dp">
+ <ImageView
+ android:id="@+id/result_encryption_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_lock_open"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/result_encryption_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Not Encrypted (set in-code)"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp" />
+ </LinearLayout>
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/result_signature_image">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
<ImageView
- android:id="@+id/ic_signature"
+ android:id="@+id/result_signature_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/signed_large" />
+ android:src="@drawable/status_signature_unverified_cutout"
+ android:layout_gravity="center_vertical" />
- <ImageView
- android:id="@+id/ic_signature_status"
+ <TextView
+ android:id="@+id/result_signature_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/overlay_error" />
- </RelativeLayout>
-
- <TextView
- android:id="@+id/mainUserId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:layout_gravity="left"
- android:text="@string/label_main_user_id"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_toRightOf="@+id/result_signature_image"
- android:textColor="@color/white" />
-
- <TextView
- android:id="@+id/mainUserIdRest"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:layout_gravity="left"
- android:text="Main User Id Rest"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_below="@+id/mainUserId"
- android:layout_toRightOf="@+id/result_signature_image"
- android:textColor="@color/white" />
-
- <Button
- android:id="@+id/lookup_key"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btn_lookup_key"
- android:drawableRight="@drawable/ic_action_download"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:background="@drawable/button_edgy"/>
-
- </RelativeLayout>
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Signed by (set in-code)"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp" />
+ </LinearLayout>
+
+ <View
+ android:id="@+id/result_signature_divider1"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginLeft="32dp"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:id="@+id/result_signature_layout"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ android:layout_marginLeft="32dp"
+ android:paddingRight="4dp"
+ style="@style/SelectableItem"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:paddingRight="4dp"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/result_signature_name"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Alice (set in-code)" />
+
+ <TextView
+ android:id="@+id/result_signature_email"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@color/tertiary_text_light"
+ android:text="alice@example.com (set in-code)"
+ android:gravity="center_vertical" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="1dip"
+ android:layout_height="match_parent"
+ android:gravity="right"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/result_signature_action"
+ android:paddingLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:text="Show"
+ android:drawableRight="@drawable/ic_action_accounts"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ style="@style/SelectableItem" />
+
+ </LinearLayout>
+
+ <View
+ android:id="@+id/result_signature_divider2"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginLeft="32dp"
+ android:background="?android:attr/listDivider" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml
index 3bd144fbf..c58e2d7e6 100644
--- a/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml
@@ -6,73 +6,109 @@
<include layout="@layout/decrypt_result_include" />
- <ScrollView
- android:fillViewport="true"
- android:paddingTop="8dp"
+ <LinearLayout
+ android:visibility="gone"
+ android:id="@+id/decrypt_text_valid"
android:layout_width="match_parent"
- android:scrollbars="vertical"
- android:layout_height="0dp"
- android:layout_weight="1">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <TextView
- android:id="@+id/decrypt_text_plaintext"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ <ScrollView
+ android:fillViewport="true"
+ android:paddingTop="8dp"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="top"
- android:hint=""
- android:textIsSelectable="true" />
+ android:scrollbars="vertical"
+ android:layout_height="0dp"
+ android:layout_weight="1">
- </ScrollView>
+ <TextView
+ android:id="@+id/decrypt_text_plaintext"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="top"
+ android:hint=""
+ android:textIsSelectable="true" />
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:background="?android:attr/listDivider" />
+ </ScrollView>
- <LinearLayout
- android:id="@+id/action_decrypt_share_plaintext"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:clickable="true"
- style="@style/SelectableItem"
- android:orientation="horizontal">
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:background="?android:attr/listDivider" />
- <TextView
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="0dp"
+ <LinearLayout
+ android:id="@+id/action_decrypt_share_plaintext"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:text="@string/btn_add_share_decrypted_text"
- android:drawableRight="@drawable/ic_action_share"
- android:drawablePadding="8dp"
- android:gravity="center_vertical"
- android:layout_weight="1" />
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:orientation="horizontal">
- <View
- android:layout_width="1dip"
- android:layout_height="match_parent"
- android:gravity="right"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="8dp"
- android:background="?android:attr/listDivider" />
+ <TextView
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:text="@string/btn_add_share_decrypted_text"
+ android:drawableRight="@drawable/ic_action_share"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:layout_weight="1" />
+
+ <View
+ android:layout_width="1dip"
+ android:layout_height="match_parent"
+ android:gravity="right"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <ImageButton
+ android:id="@+id/action_decrypt_copy_plaintext"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:src="@drawable/ic_action_copy"
+ android:layout_gravity="center_vertical"
+ style="@style/SelectableItem" />
+
+ </LinearLayout>
- <ImageButton
- android:id="@+id/action_decrypt_copy_plaintext"
+ </LinearLayout>
+
+ <LinearLayout
+ android:visibility="gone"
+ android:id="@+id/decrypt_text_invalid"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_vertical">
+
+ <TextView
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/decrypt_invalid_text"
android:padding="8dp"
- android:src="@drawable/ic_action_copy"
- android:layout_gravity="center_vertical"
- style="@style/SelectableItem" />
+ android:layout_gravity="center"
+ android:textColor="@color/android_red_dark" />
+ <Button
+ android:id="@+id/decrypt_text_invalid_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/button_edgy"
+ android:textColor="@color/android_red_dark"
+ android:text="@string/decrypt_invalid_button"
+ android:layout_gravity="center_horizontal" />
</LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/key_list_content.xml b/OpenKeychain/src/main/res/layout/key_list_content.xml
index 66b009c78..8ab63610f 100644
--- a/OpenKeychain/src/main/res/layout/key_list_content.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_content.xml
@@ -6,17 +6,42 @@
<include layout="@layout/notify_area" />
- <FrameLayout
+ <LinearLayout
android:id="@+id/content_frame"
android:layout_marginLeft="@dimen/drawer_content_padding"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation="vertical"
+ android:background="@color/holo_gray_bright"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Spinner
+ android:id="@+id/key_list_filter_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dp"
+ android:layout_marginRight="12dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ </LinearLayout>
+
<fragment
android:id="@+id/key_list_fragment"
android:name="org.sufficientlysecure.keychain.ui.KeyListFragment"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </FrameLayout>
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ </LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/key_list_item.xml b/OpenKeychain/src/main/res/layout/key_list_item.xml
index a7a195ffa..bddc2ad97 100644
--- a/OpenKeychain/src/main/res/layout/key_list_item.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_item.xml
@@ -39,7 +39,7 @@
</LinearLayout>
<ImageView
- android:id="@+id/status_image"
+ android:id="@+id/status_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/OpenKeychain/src/main/res/layout/keyspinner_item.xml b/OpenKeychain/src/main/res/layout/keyspinner_item.xml
index 411ceefdf..b75bb808e 100644
--- a/OpenKeychain/src/main/res/layout/keyspinner_item.xml
+++ b/OpenKeychain/src/main/res/layout/keyspinner_item.xml
@@ -2,12 +2,12 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="24dp"
android:gravity="center_vertical"
android:singleLine="true"
android:orientation="horizontal"
android:descendantFocusability="blocksDescendants"
- android:focusable="false">
+ android:focusable="false"
+ android:minHeight="44dip">
<LinearLayout
android:layout_width="0dip"
@@ -17,9 +17,7 @@
android:focusable="true"
android:orientation="vertical"
android:paddingLeft="8dp"
- android:paddingRight="4dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp">
+ android:paddingRight="4dp">
<TextView
android:id="@+id/keyspinner_key_name"
@@ -44,7 +42,7 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<ImageView
@@ -53,6 +51,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_signature_revoked_cutout"
- android:padding="16dp" />
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp" />
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/log_display_fragment.xml b/OpenKeychain/src/main/res/layout/log_display_fragment.xml
deleted file mode 100644
index 442e72d09..000000000
--- a/OpenKeychain/src/main/res/layout/log_display_fragment.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ListView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/log_text" />
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/log_display_item.xml b/OpenKeychain/src/main/res/layout/log_display_item.xml
index 35489afed..d3938aaf0 100644
--- a/OpenKeychain/src/main/res/layout/log_display_item.xml
+++ b/OpenKeychain/src/main/res/layout/log_display_item.xml
@@ -1,22 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal" android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:id="@+id/log_img"
- android:minWidth="10dp"
- android:background="@color/bg_gray" />
-
- <TextView
- android:layout_width="wrap_content"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:id="@+id/log_img"
+ android:minWidth="10dp"
+ android:background="@color/bg_gray" />
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="Log Entry Text"
+ android:id="@+id/log_text"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_marginLeft="8dp"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"/>
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/log_sub"
+ android:src="@drawable/ic_action_view_as_list"
+ android:layout_marginBottom="4dp"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:gravity="center_vertical"
+ android:layout_marginTop="4dp" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Log Entry Text"
- android:id="@+id/log_text"
- android:layout_marginTop="4dp"
- android:layout_marginBottom="4dp"
- android:layout_marginLeft="8dp" />
+ android:id="@+id/log_second"
+ android:layout_marginLeft="8dip">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:id="@+id/log_second_img"
+ android:minWidth="10dp"
+ android:background="@color/bg_gray" />
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="Log Entry Text"
+ android:id="@+id/log_second_text"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_marginLeft="8dp"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"/>
+ </LinearLayout>
+
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/multi_certify_item.xml b/OpenKeychain/src/main/res/layout/multi_certify_item.xml
new file mode 100644
index 000000000..c578473d3
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/multi_certify_item.xml
@@ -0,0 +1,88 @@
+<?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="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="vertical"
+ android:singleLine="true">
+
+ <LinearLayout android:id="@+id/user_id_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clickable="true"
+ android:layout_marginLeft="8dip"
+ android:layout_marginTop="16dip">
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/user_id_header_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="ID: 0123 4567 890a bcde"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:paddingLeft="24dip"
+ android:paddingRight="24dip"
+ android:layout_marginBottom="4dip"/>
+
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/user_id_body"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:singleLine="true"
+ android:layout_marginLeft="8dip"
+ android:layout_marginTop="4dip">
+
+ <CheckBox
+ android:id="@+id/user_id_item_check_box"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:clickable="false"
+ android:focusable="false" />
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_gravity="center_vertical"
+ android:layout_width="0dip"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:minHeight="48dip">
+
+ <TextView
+ android:id="@+id/user_id_item_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="alice@example.com"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_weight="1"
+ android:gravity="center_vertical"/>
+
+ <TextView
+ android:id="@+id/user_id_item_addresses"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="alice@example.com\na-lyc@example.com"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_marginLeft="20dip"
+ />
+
+ </LinearLayout>
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/multi_certify_key_activity.xml b/OpenKeychain/src/main/res/layout/multi_certify_key_activity.xml
new file mode 100644
index 000000000..c3eaed9a8
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/multi_certify_key_activity.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/notify_area" />
+
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_marginLeft="@dimen/drawer_content_padding"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <fragment
+ android:id="@+id/multi_certify_key_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.MultiCertifyKeyFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </FrameLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/multi_certify_key_fragment.xml b/OpenKeychain/src/main/res/layout/multi_certify_key_fragment.xml
new file mode 100644
index 000000000..d2335cbe9
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/multi_certify_key_fragment.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="8dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/imageView"
+ android:src="@drawable/ic_action_person"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/certify_text"
+ android:id="@+id/textView"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/section_uids_to_certify" />
+
+ <org.sufficientlysecure.keychain.ui.widget.FixedListView
+ android:id="@+id/view_key_user_ids"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_certify"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:paddingLeft="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/add_keys_my_key"
+ android:paddingRight="8dp"
+ android:gravity="center_vertical" />
+
+ <org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner
+ android:id="@+id/certify_key_spinner"
+ android:minHeight="56dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
+
+ <CheckBox
+ android:id="@+id/sign_key_upload_checkbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="false"
+ android:text="@string/label_send_key"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:id="@+id/certify_key_certify_button"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ android:paddingRight="4dp"
+ style="@style/SelectableItem"
+ android:orientation="horizontal">
+
+ <TextView
+ android:paddingLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:text="@string/key_view_action_certify"
+ android:layout_weight="1"
+ android:gravity="center_vertical" />
+
+ <!-- separate ImageView required for recoloring -->
+ <ImageView
+ android:id="@+id/certify_key_action_certify_image"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:src="@drawable/status_signature_verified_cutout"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginBottom="4dp"
+ android:background="?android:attr/listDivider" />
+
+ </LinearLayout>
+
+</ScrollView> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml b/OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml
index 85ed92ef7..57ca9b9d8 100644
--- a/OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml
+++ b/OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml
@@ -43,7 +43,6 @@
android:paddingLeft="16dip"
android:singleLine="true"
android:ellipsize="end"
- android:typeface="monospace"
android:layout_marginTop="-4dip" />
</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/select_key_item.xml b/OpenKeychain/src/main/res/layout/select_key_item.xml
index 4fe80c7e1..b6cbfd973 100644
--- a/OpenKeychain/src/main/res/layout/select_key_item.xml
+++ b/OpenKeychain/src/main/res/layout/select_key_item.xml
@@ -42,22 +42,16 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0xBBBBBBBBBBBBBBB"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:typeface="monospace" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
-
- <TextView
- android:gravity="right"
- android:paddingLeft="4dp"
- android:minWidth="90dip"
- android:id="@+id/status"
- android:paddingTop="4dp"
+ <ImageView
+ android:id="@+id/status_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="expired"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textStyle="italic"
- android:layout_gravity="right" />
+ android:layout_gravity="center"
+ android:src="@drawable/status_signature_revoked_cutout"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp" />
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/select_secret_key_activity.xml b/OpenKeychain/src/main/res/layout/select_secret_key_activity.xml
deleted file mode 100644
index c4cdd7576..000000000
--- a/OpenKeychain/src/main/res/layout/select_secret_key_activity.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_centerHorizontal="true" >
-
- <FrameLayout
- android:id="@+id/select_secret_key_fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
-</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/select_secret_key_layout_fragment.xml b/OpenKeychain/src/main/res/layout/select_secret_key_layout_fragment.xml
deleted file mode 100644
index e5fd3f9f2..000000000
--- a/OpenKeychain/src/main/res/layout/select_secret_key_layout_fragment.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?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="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/select_secret_key_select_key_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="4dp"
- android:layout_marginRight="4dp"
- android:layout_marginTop="4dp"
- android:text="@string/api_settings_select_key"
- android:drawableLeft="@drawable/ic_action_accounts"
- android:background="@drawable/button_edgy"
- android:textSize="14sp"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginLeft="4dp"
- android:layout_marginTop="4dp"
- android:orientation="vertical"
- android:paddingLeft="4dp">
-
- <!-- Has been made focusable to display error messages with setError -->
- <TextView
- android:id="@+id/select_secret_key_user_id"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_marginRight="5dip"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- <TextView
- android:id="@+id/select_secret_key_user_id_rest"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:ellipsize="end"
- android:singleLine="true"
- android:layout_marginRight="5dip"
- android:text=""
- android:visibility="gone"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- <TextView
- android:id="@+id/select_secret_key_master_key_hex"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:visibility="gone"
- android:layout_marginRight="15dip" />
-
- <TextView
- android:id="@+id/no_key_selected"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="@string/api_settings_no_key"
- android:layout_marginTop="15dp" />
-
- </LinearLayout>
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
index 6bcb216d7..691ee357d 100644
--- a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
@@ -147,7 +147,6 @@
android:id="@+id/view_key_action_update"
android:paddingLeft="8dp"
android:paddingRight="8dp"
- android:layout_marginBottom="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/OpenKeychain/src/main/res/menu/key_list.xml b/OpenKeychain/src/main/res/menu/key_list.xml
index 6e571243d..3e7d6fc9f 100644
--- a/OpenKeychain/src/main/res/menu/key_list.xml
+++ b/OpenKeychain/src/main/res/menu/key_list.xml
@@ -7,18 +7,23 @@
android:title="@string/menu_search"
android:icon="@drawable/ic_action_search"
app:actionViewClass="android.support.v7.widget.SearchView"
- app:showAsAction="collapseActionView|ifRoom" />
+ app:showAsAction="collapseActionView|always" />
+
+ <item
+ android:id="@+id/menu_key_list_search_cloud"
+ app:showAsAction="always|withText"
+ android:icon="@drawable/ic_action_search_cloud"
+ android:title="@string/menu_add_keys" />
<item
android:id="@+id/menu_key_list_add"
- app:showAsAction="ifRoom|withText"
- android:icon="@drawable/ic_action_add_person"
+ app:showAsAction="always|withText"
+ android:icon="@drawable/ic_action_new_account"
android:title="@string/menu_add_keys" />
<item
android:id="@+id/menu_key_list_export"
- app:showAsAction="ifRoom|withText"
- android:icon="@drawable/ic_action_import_export"
+ app:showAsAction="never"
android:title="@string/menu_export_all_keys" />
<item
diff --git a/OpenKeychain/src/main/res/values-cs/strings.xml b/OpenKeychain/src/main/res/values-cs/strings.xml
index 9f676c6f9..3276eefed 100644
--- a/OpenKeychain/src/main/res/values-cs/strings.xml
+++ b/OpenKeychain/src/main/res/values-cs/strings.xml
@@ -229,7 +229,7 @@
<string name="error_generic_report_bug">Nastala obecná chyba, prosím vytvořte nový bug report pro OpenKeychain.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Špatný podpis!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Neznámý veřejný klíč</string>
+ <string name="decrypt_result_signature_missing_key">Neznámý veřejný klíč</string>
<string name="decrypt_result_signature_uncertified">Validní podpis (neověřen)</string>
<string name="decrypt_result_signature_certified">Validní podpis (ověřen)</string>
<string name="decrypt_result_decrypted">Úspěšně rozšifrováno</string>
@@ -329,7 +329,7 @@
<string name="view_log">Zobrazit log</string>
<string name="import_error_nothing">Nic k importu</string>
<string name="import_error_nothing_cancelled">Import zrušen.</string>
- <string name="import_with_warnings">, s varováními</string>
+ <string name="with_warnings">, s varováními</string>
<plurals name="import_error">
<item quantity="one">Import selhal!</item>
<item quantity="few">Import %d klíčů selhal!</item>
@@ -397,10 +397,10 @@
<string name="key_view_tab_certs">Certifikáty</string>
<string name="user_id_info_revoked_title">Zneplatněno</string>
<string name="user_id_info_revoked_text">Tato identity byla zneplatněna vlastníkem klíče. Klíč již není platný.</string>
- <string name="user_id_info_verified_title">Ověřeno</string>
- <string name="user_id_info_verified_text">Tato identita byla ověřena.</string>
- <string name="user_id_info_not_verified_title">Neověřeno</string>
- <string name="user_id_info_not_verified_text">Tato identita nebyla ještě ověřena. Nemůžete si být jisti, jestli identita opravdu odpovídá určité osobě.</string>
+ <string name="user_id_info_certified_title">Ověřeno</string>
+ <string name="user_id_info_certified_text">Tato identita byla ověřena.</string>
+ <string name="user_id_info_uncertified_title">Neověřeno</string>
+ <string name="user_id_info_uncertified_text">Tato identita nebyla ještě ověřena. Nemůžete si být jisti, jestli identita opravdu odpovídá určité osobě.</string>
<string name="user_id_info_invalid_title">Neplatná</string>
<string name="user_id_info_invalid_text">S touto identitou je něco v nepořádku!</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml
index 1ff3fa024..3753068bc 100644
--- a/OpenKeychain/src/main/res/values-de/strings.xml
+++ b/OpenKeychain/src/main/res/values-de/strings.xml
@@ -246,7 +246,7 @@
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Ungültige Signatur!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Unbekannter öffentlicher Schlüssel</string>
+ <string name="decrypt_result_signature_missing_key">Unbekannter öffentlicher Schlüssel</string>
<string name="decrypt_result_signature_uncertified">Gültige Signatur (nicht beglaubigt)</string>
<string name="decrypt_result_signature_certified">Gültige Signatur (beglaubigt)</string>
<string name="decrypt_result_decrypted">Erfolgreich entschlüsselt</string>
@@ -369,8 +369,8 @@
<string name="view_log">Log ansehen</string>
<string name="import_error_nothing">Nichts zu importieren.</string>
<string name="import_error_nothing_cancelled">Import abgebrochen.</string>
- <string name="import_with_warnings">, mit Warnungen</string>
- <string name="import_with_cancelled">. bis abgebrochen wurde</string>
+ <string name="with_warnings">, mit Warnungen</string>
+ <string name="with_cancelled">. bis abgebrochen wurde</string>
<plurals name="import_error">
<item quantity="one">Importieren fehlgeschlagen!</item>
<item quantity="other">Importieren von %d Schlüsseln fehlgeschlagen!</item>
@@ -438,10 +438,10 @@
<string name="key_view_tab_certs">Beglaubigungen</string>
<string name="user_id_info_revoked_title">Wiederrufen</string>
<string name="user_id_info_revoked_text">Diese Identität wurde durch den Schlüsselinhaber wiederrufen. Sie ist nicht mehr gültig.</string>
- <string name="user_id_info_verified_title">Überprüft</string>
- <string name="user_id_info_verified_text">Diese Identität wurde überprüft.</string>
- <string name="user_id_info_not_verified_title">Nicht überprüft</string>
- <string name="user_id_info_not_verified_text">Diese Identität wurde noch nicht verifiziert. Du kannst nicht sicher sein, ob diese Identität wirklich zu einer bestimmten Person gehört.</string>
+ <string name="user_id_info_certified_title">Überprüft</string>
+ <string name="user_id_info_certified_text">Diese Identität wurde überprüft.</string>
+ <string name="user_id_info_uncertified_title">Nicht überprüft</string>
+ <string name="user_id_info_uncertified_text">Diese Identität wurde noch nicht verifiziert. Du kannst nicht sicher sein, ob diese Identität wirklich zu einer bestimmten Person gehört.</string>
<string name="user_id_info_invalid_title">Ungültig</string>
<string name="user_id_info_invalid_text">Irgend etwas ist mit dieser Identität nicht in Ordnung!</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml
index 968ece35a..6bc2abb6a 100644
--- a/OpenKeychain/src/main/res/values-es/strings.xml
+++ b/OpenKeychain/src/main/res/values-es/strings.xml
@@ -238,7 +238,7 @@
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">¡Firma no válida!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Clave pública desconocida</string>
+ <string name="decrypt_result_signature_missing_key">Clave pública desconocida</string>
<string name="decrypt_result_signature_uncertified">Firma válida (no certificada)</string>
<string name="decrypt_result_signature_certified">Firma válida (certificada)</string>
<string name="decrypt_result_decrypted">Descifrado con éxito</string>
@@ -357,8 +357,8 @@
<string name="view_log">Ver registro (log)</string>
<string name="import_error_nothing">No hay nada que importar.</string>
<string name="import_error_nothing_cancelled">Importación cancelada.</string>
- <string name="import_with_warnings">, con advertencias</string>
- <string name="import_with_cancelled">, hasta que este cancelada</string>
+ <string name="with_warnings">, con advertencias</string>
+ <string name="with_cancelled">, hasta que este cancelada</string>
<!--Intent labels-->
<string name="intent_decrypt_file">Descifrar archivo con OpenKeychain</string>
<string name="intent_import_key">Importar clave con OpenKeychain</string>
@@ -421,10 +421,10 @@
<string name="key_view_tab_certs">Certificados</string>
<string name="user_id_info_revoked_title">Revocada</string>
<string name="user_id_info_revoked_text">Esta identidad ha sido revocada por el propietario de la clave. En adelante no es válida.</string>
- <string name="user_id_info_verified_title">Verificada</string>
- <string name="user_id_info_verified_text">Esta identidad ha sido verificada</string>
- <string name="user_id_info_not_verified_title">No verificada</string>
- <string name="user_id_info_not_verified_text">Esta identidad no se ha verificado aún. No puede estar seguro de si la identidad realmente corresponde a una persona en concreto.</string>
+ <string name="user_id_info_certified_title">Verificada</string>
+ <string name="user_id_info_certified_text">Esta identidad ha sido verificada</string>
+ <string name="user_id_info_uncertified_title">No verificada</string>
+ <string name="user_id_info_uncertified_text">Esta identidad no se ha verificado aún. No puede estar seguro de si la identidad realmente corresponde a una persona en concreto.</string>
<string name="user_id_info_invalid_title">No válido</string>
<string name="user_id_info_invalid_text">¡Algo está mal con esta identidad!</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml
index 91940c7c3..e1cb9ec24 100644
--- a/OpenKeychain/src/main/res/values-fr/strings.xml
+++ b/OpenKeychain/src/main/res/values-fr/strings.xml
@@ -238,7 +238,7 @@
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Signature invalide !</string>
- <string name="decrypt_result_signature_unknown_pub_key">Clef publique inconnue</string>
+ <string name="decrypt_result_signature_missing_key">Clef publique inconnue</string>
<string name="decrypt_result_signature_uncertified">Signature valide (non certifiée)</string>
<string name="decrypt_result_signature_certified">Signature valide (certifiée)</string>
<string name="decrypt_result_decrypted">Déchiffré avec succès</string>
@@ -357,8 +357,8 @@
<string name="view_log">Consulter le journal</string>
<string name="import_error_nothing">Rien à importer.</string>
<string name="import_error_nothing_cancelled">Importation annulée.</string>
- <string name="import_with_warnings">, avec des avertissements</string>
- <string name="import_with_cancelled">, jusqu\'à l\'annulation</string>
+ <string name="with_warnings">, avec des avertissements</string>
+ <string name="with_cancelled">, jusqu\'à l\'annulation</string>
<!--Intent labels-->
<string name="intent_decrypt_file">Déchiffrer le fichier avec OpenKeychain</string>
<string name="intent_import_key">Importer la clef avec OpenKeychain</string>
@@ -421,10 +421,10 @@
<string name="key_view_tab_certs">Certificats</string>
<string name="user_id_info_revoked_title">Révoquée</string>
<string name="user_id_info_revoked_text">Cette identité a été révoquée par le propriétaire de la clef. Elle n\'est plus valide.</string>
- <string name="user_id_info_verified_title">Vérifiée</string>
- <string name="user_id_info_verified_text">Cette identité a été vérifiée.</string>
- <string name="user_id_info_not_verified_title">Non vérifiée</string>
- <string name="user_id_info_not_verified_text">Cette identité n\'a pas encore été vérifiée. Vous ne pouvez pas être certain si l\'identité corresponds vraiment à une personne spécifique.</string>
+ <string name="user_id_info_certified_title">Vérifiée</string>
+ <string name="user_id_info_certified_text">Cette identité a été vérifiée.</string>
+ <string name="user_id_info_uncertified_title">Non vérifiée</string>
+ <string name="user_id_info_uncertified_text">Cette identité n\'a pas encore été vérifiée. Vous ne pouvez pas être certain si l\'identité corresponds vraiment à une personne spécifique.</string>
<string name="user_id_info_invalid_title">Invalide</string>
<string name="user_id_info_invalid_text">Quelque chose ne va pas avec cette identité !</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml
index 5c652fd73..5b79461d5 100644
--- a/OpenKeychain/src/main/res/values-it/strings.xml
+++ b/OpenKeychain/src/main/res/values-it/strings.xml
@@ -245,7 +245,7 @@ Non potrai annullare!</string>
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Firma non valida!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Chiave pubblica sconosciuta</string>
+ <string name="decrypt_result_signature_missing_key">Chiave pubblica sconosciuta</string>
<string name="decrypt_result_signature_uncertified">Firma valida (non certificata)</string>
<string name="decrypt_result_signature_certified">Firma valida (certificata)</string>
<string name="decrypt_result_decrypted">Decodificato correttamente</string>
@@ -362,7 +362,7 @@ Non potrai annullare!</string>
<string name="view_log">Mostra registro</string>
<string name="import_error_nothing">Niente da importare</string>
<string name="import_error_nothing_cancelled">Importazione cancellata.</string>
- <string name="import_with_warnings">, con avvisi</string>
+ <string name="with_warnings">, con avvisi</string>
<!--Intent labels-->
<string name="intent_decrypt_file">Decodifica File con OpenKeychain</string>
<string name="intent_import_key">Importa Chiave con OpenKeychain</string>
@@ -426,10 +426,10 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="key_view_tab_certs">Certificati</string>
<string name="user_id_info_revoked_title">Revocato</string>
<string name="user_id_info_revoked_text">Questa identità è stata revocata dal suo proprietario. Non è più valida.</string>
- <string name="user_id_info_verified_title">Verificato</string>
- <string name="user_id_info_verified_text">Questa identità è stata verificata.</string>
- <string name="user_id_info_not_verified_title">Non verificato</string>
- <string name="user_id_info_not_verified_text">Questa identità non è stata ancora verificata. Non puoi esser sicuro che l\'identità corrisponda veramente ad una specifica persona.</string>
+ <string name="user_id_info_certified_title">Verificato</string>
+ <string name="user_id_info_certified_text">Questa identità è stata verificata.</string>
+ <string name="user_id_info_uncertified_title">Non verificato</string>
+ <string name="user_id_info_uncertified_text">Questa identità non è stata ancora verificata. Non puoi esser sicuro che l\'identità corrisponda veramente ad una specifica persona.</string>
<string name="user_id_info_invalid_title">Non valido</string>
<string name="user_id_info_invalid_text">C\'è qualcosa che non va con questa identità!</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml
index d1a90ff8c..86927d621 100644
--- a/OpenKeychain/src/main/res/values-ja/strings.xml
+++ b/OpenKeychain/src/main/res/values-ja/strings.xml
@@ -254,7 +254,7 @@
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">不正な署名です!</string>
- <string name="decrypt_result_signature_unknown_pub_key">不明な公開鍵</string>
+ <string name="decrypt_result_signature_missing_key">不明な公開鍵</string>
<string name="decrypt_result_signature_uncertified">正しい署名 (未証明)</string>
<string name="decrypt_result_signature_certified">正しい署名 (証明ずみ)</string>
<string name="decrypt_result_decrypted">復号化に成功した</string>
@@ -371,8 +371,8 @@
<string name="view_log">ログを見る</string>
<string name="import_error_nothing">インポートするものがありません。</string>
<string name="import_error_nothing_cancelled">インポートをキャンセルしました。</string>
- <string name="import_with_warnings">、とワーニング</string>
- <string name="import_with_cancelled">、キャンセルされるまで</string>
+ <string name="with_warnings">、とワーニング</string>
+ <string name="with_cancelled">、キャンセルされるまで</string>
<plurals name="import_error">
<item quantity="other">%d 個の鍵のインポート失敗!</item>
</plurals>
@@ -443,10 +443,10 @@
<string name="key_view_tab_certs">証明</string>
<string name="user_id_info_revoked_title">破棄</string>
<string name="user_id_info_revoked_text">このIDは鍵の所有者により破棄されています。もう適正ではありません。</string>
- <string name="user_id_info_verified_title">検証</string>
- <string name="user_id_info_verified_text">このIDは検証されています。</string>
- <string name="user_id_info_not_verified_title">未検証</string>
- <string name="user_id_info_not_verified_text">このIDはまだ検証されていません。IDが本当に特定の人に対応している場合を、あなたは確認することができません。</string>
+ <string name="user_id_info_certified_title">検証</string>
+ <string name="user_id_info_certified_text">このIDは検証されています。</string>
+ <string name="user_id_info_uncertified_title">未検証</string>
+ <string name="user_id_info_uncertified_text">このIDはまだ検証されていません。IDが本当に特定の人に対応している場合を、あなたは確認することができません。</string>
<string name="user_id_info_invalid_title">不適正</string>
<string name="user_id_info_invalid_text">このIDではなにかしら問題があります!</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml
index c076bbde4..e45b3b62f 100644
--- a/OpenKeychain/src/main/res/values-nl/strings.xml
+++ b/OpenKeychain/src/main/res/values-nl/strings.xml
@@ -174,7 +174,7 @@
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Ongeldige handtekening!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Onbekende publieke sleutel</string>
+ <string name="decrypt_result_signature_missing_key">Onbekende publieke sleutel</string>
<string name="decrypt_result_signature_uncertified">Geldige handtekening (ongecertificeerd)</string>
<string name="decrypt_result_signature_certified">Geldige handtekening (gecertificeerd)</string>
<string name="decrypt_result_decrypted">Succesvol gedecodeerd</string>
diff --git a/OpenKeychain/src/main/res/values-pl/strings.xml b/OpenKeychain/src/main/res/values-pl/strings.xml
index 8effd7f98..e4653d403 100644
--- a/OpenKeychain/src/main/res/values-pl/strings.xml
+++ b/OpenKeychain/src/main/res/values-pl/strings.xml
@@ -161,7 +161,7 @@
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Nieprawidłowy podpis!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Nieznany klucz publiczny</string>
+ <string name="decrypt_result_signature_missing_key">Nieznany klucz publiczny</string>
<string name="decrypt_result_signature_uncertified">Podpis prawidłowy (bez certyfikatu)</string>
<string name="decrypt_result_signature_certified">Podpis prawidłowy (z certyfikatem)</string>
<string name="decrypt_result_decrypted">Odszyfrowano pomyślnie</string>
diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml
index 5ce53a9f1..303efdf0a 100644
--- a/OpenKeychain/src/main/res/values-ru/strings.xml
+++ b/OpenKeychain/src/main/res/values-ru/strings.xml
@@ -209,7 +209,7 @@
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Неверная подпись!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Неизвестный ключ</string>
+ <string name="decrypt_result_signature_missing_key">Неизвестный ключ</string>
<string name="decrypt_result_signature_uncertified">Верная подпись (не сертифицирована)</string>
<string name="decrypt_result_signature_certified">Верная подпись (сертифицирована)</string>
<string name="decrypt_result_decrypted">Успешно расшифровано</string>
@@ -311,7 +311,7 @@
</plurals>
<string name="view_log">Смотреть журнал</string>
<string name="import_error_nothing">Нет данных для импорта.</string>
- <string name="import_with_warnings">, с предупреждениями</string>
+ <string name="with_warnings">, с предупреждениями</string>
<!--Intent labels-->
<string name="intent_decrypt_file">OpenKeychain: Расшифровать файл</string>
<string name="intent_import_key">OpenKeychain: Импортировать ключ</string>
@@ -361,9 +361,9 @@
<string name="key_view_tab_keys">Доп. ключи</string>
<string name="key_view_tab_certs">Сертификация</string>
<string name="user_id_info_revoked_title">Аннулировано</string>
- <string name="user_id_info_verified_title">Подтверждено</string>
- <string name="user_id_info_not_verified_title">Не подтверждено</string>
- <string name="user_id_info_not_verified_text">Этот идентификатор не был заверен. Нет гарантии, что он принадлежит этому человеку.</string>
+ <string name="user_id_info_certified_title">Подтверждено</string>
+ <string name="user_id_info_uncertified_title">Не подтверждено</string>
+ <string name="user_id_info_uncertified_text">Этот идентификатор не был заверен. Нет гарантии, что он принадлежит этому человеку.</string>
<string name="user_id_info_invalid_title">Недействительно</string>
<string name="user_id_info_invalid_text">Что-то не так с идентификатором!</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml
index 491bf5cf5..9b92ab14f 100644
--- a/OpenKeychain/src/main/res/values-sl/strings.xml
+++ b/OpenKeychain/src/main/res/values-sl/strings.xml
@@ -226,7 +226,7 @@
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Neveljaven podpis!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Neznan javni ključ</string>
+ <string name="decrypt_result_signature_missing_key">Neznan javni ključ</string>
<string name="decrypt_result_signature_uncertified">Veljaven podpis (neoverjen)</string>
<string name="decrypt_result_signature_certified">Veljaven podpis (overjen)</string>
<string name="decrypt_result_decrypted">Uspešno dešifrirano</string>
@@ -319,8 +319,8 @@
<string name="view_log">Poglej dnevnik</string>
<string name="import_error_nothing">Nič za uvoziti.</string>
<string name="import_error_nothing_cancelled">Uvoz preklican.</string>
- <string name="import_with_warnings">, z opozorilom</string>
- <string name="import_with_cancelled"> do preklica</string>
+ <string name="with_warnings">, z opozorilom</string>
+ <string name="with_cancelled"> do preklica</string>
<!--Intent labels-->
<string name="intent_decrypt_file">Dešifriraj datoteko z OpenKeychain</string>
<string name="intent_import_key">Uvozi ključ z OpenKeychain</string>
@@ -380,9 +380,9 @@
<string name="key_view_tab_certs">Certifikati</string>
<string name="user_id_info_revoked_title">Preklican</string>
<string name="user_id_info_revoked_text">Lastnik ključa je preklical to identiteto. Ta ni več veljavna.</string>
- <string name="user_id_info_verified_title">Preverjen</string>
- <string name="user_id_info_verified_text">Identiteta je bila preverjena</string>
- <string name="user_id_info_not_verified_title">Nepreverjen</string>
+ <string name="user_id_info_certified_title">Preverjen</string>
+ <string name="user_id_info_certified_text">Identiteta je bila preverjena</string>
+ <string name="user_id_info_uncertified_title">Nepreverjen</string>
<string name="user_id_info_invalid_title">Neveljaven</string>
<!--Edit key-->
<string name="edit_key_action_change_passphrase">Zamenjaj geslo</string>
diff --git a/OpenKeychain/src/main/res/values-sr/strings.xml b/OpenKeychain/src/main/res/values-sr/strings.xml
index c1121ef4d..d77809010 100644
--- a/OpenKeychain/src/main/res/values-sr/strings.xml
+++ b/OpenKeychain/src/main/res/values-sr/strings.xml
@@ -231,7 +231,7 @@
<string name="error_generic_report_bug">Дошло је до опште грешке, направите нови извештај о грешци за Отворени кључарник.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Неисправан потпис!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Непознат јавни кључ</string>
+ <string name="decrypt_result_signature_missing_key">Непознат јавни кључ</string>
<string name="decrypt_result_signature_uncertified">Исправан потпис (неоверен)</string>
<string name="decrypt_result_signature_certified">Исправан потпис (оверен)</string>
<string name="decrypt_result_decrypted">Успешно дешифровано</string>
@@ -330,8 +330,8 @@
<string name="view_log">Прикажи дневник</string>
<string name="import_error_nothing">Нема ништа за увоз.</string>
<string name="import_error_nothing_cancelled">Увоз је отказан.</string>
- <string name="import_with_warnings">, са упозорењима</string>
- <string name="import_with_cancelled">, док није отказано</string>
+ <string name="with_warnings">, са упозорењима</string>
+ <string name="with_cancelled">, док није отказано</string>
<!--Intent labels-->
<string name="intent_decrypt_file">Дешифруј фајл помоћу Отвореног кључарника</string>
<string name="intent_import_key">Увези кључ у Отворени кључарник</string>
@@ -393,10 +393,10 @@
<string name="key_view_tab_certs">Сертификати</string>
<string name="user_id_info_revoked_title">Опозван</string>
<string name="user_id_info_revoked_text">Власник кључа је опозвао овај идентитет. Више није исправан.</string>
- <string name="user_id_info_verified_title">Оверен</string>
- <string name="user_id_info_verified_text">Овај идентитет није оверен.</string>
- <string name="user_id_info_not_verified_title">Није оверен</string>
- <string name="user_id_info_not_verified_text">Овај идентитет још није оверен. Не можете бити сигурни да идентитет заиста одговара одређеној особи.</string>
+ <string name="user_id_info_certified_title">Оверен</string>
+ <string name="user_id_info_certified_text">Овај идентитет није оверен.</string>
+ <string name="user_id_info_uncertified_title">Није оверен</string>
+ <string name="user_id_info_uncertified_text">Овај идентитет још није оверен. Не можете бити сигурни да идентитет заиста одговара одређеној особи.</string>
<string name="user_id_info_invalid_title">Неисправан</string>
<string name="user_id_info_invalid_text">Нешто није у реду са овим идентитетом!</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values-tr/strings.xml b/OpenKeychain/src/main/res/values-tr/strings.xml
index 379e2af3c..6a96e4f5e 100644
--- a/OpenKeychain/src/main/res/values-tr/strings.xml
+++ b/OpenKeychain/src/main/res/values-tr/strings.xml
@@ -214,7 +214,7 @@
<string name="error_generic_report_bug">Genel bir hata oluştu. Lütfen OpenKeychain için bir hata raporu oluşturun.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Geçersiz imza!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Bilinmeyen açık anahtar</string>
+ <string name="decrypt_result_signature_missing_key">Bilinmeyen açık anahtar</string>
<string name="decrypt_result_signature_uncertified">Geçerli imza (sertifikasız)</string>
<string name="decrypt_result_signature_certified">Geçerli imza (sertifalı)</string>
<string name="decrypt_result_decrypted">Başarıyla çözümlendi</string>
@@ -353,10 +353,10 @@
<string name="key_view_tab_share">Paylaş</string>
<string name="key_view_tab_keys">Alt anahtarlar</string>
<string name="key_view_tab_certs">Sertifikalar</string>
- <string name="user_id_info_verified_title">Doğrulanmış</string>
- <string name="user_id_info_verified_text">Kimlik doğrulandı.</string>
- <string name="user_id_info_not_verified_title">Doğrulanmamış</string>
- <string name="user_id_info_not_verified_text">Bu kimlik henüz doğrulanmadı. Bu kimliğin belirli bir kişiye ait olduğundan emin olamazsınız.</string>
+ <string name="user_id_info_certified_title">Doğrulanmış</string>
+ <string name="user_id_info_certified_text">Kimlik doğrulandı.</string>
+ <string name="user_id_info_uncertified_title">Doğrulanmamış</string>
+ <string name="user_id_info_uncertified_text">Bu kimlik henüz doğrulanmadı. Bu kimliğin belirli bir kişiye ait olduğundan emin olamazsınız.</string>
<string name="user_id_info_invalid_title">Geçersiz</string>
<string name="user_id_info_invalid_text">Bu kimlikle ilgili yanlış olan bazı şeyler var!</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml
index f38a542be..46d5f59c8 100644
--- a/OpenKeychain/src/main/res/values-uk/strings.xml
+++ b/OpenKeychain/src/main/res/values-uk/strings.xml
@@ -208,7 +208,7 @@
</plurals>
<!--results shown after decryption/verification-->
<string name="decrypt_result_invalid_signature">Невірний підпис!</string>
- <string name="decrypt_result_signature_unknown_pub_key">Невідомий відкритий ключ</string>
+ <string name="decrypt_result_signature_missing_key">Невідомий відкритий ключ</string>
<string name="decrypt_result_signature_uncertified">Дійсний підпис (несертифікований)</string>
<string name="decrypt_result_signature_certified">Дійсний підпис (сертифікований)</string>
<string name="decrypt_result_decrypted">Успішно розшифровано.</string>
@@ -327,7 +327,7 @@
</plurals>
<string name="view_log">Переглянути журнал</string>
<string name="import_error_nothing">Нема що імпортувати.</string>
- <string name="import_with_warnings">, із застереженнями</string>
+ <string name="with_warnings">, із застереженнями</string>
<!--Intent labels-->
<string name="intent_decrypt_file">Розшифрувати файл з OpenKeychain</string>
<string name="intent_import_key">Імпортувати ключ з OpenKeychain</string>
@@ -383,10 +383,10 @@
<string name="key_view_tab_certs">Сертифікати</string>
<string name="user_id_info_revoked_title">Відхилено</string>
<string name="user_id_info_revoked_text">Ця сутність вже відкликана власником ключа. Вона більше не дійсна.</string>
- <string name="user_id_info_verified_title">Перевірено</string>
- <string name="user_id_info_verified_text">Не перевірено</string>
- <string name="user_id_info_not_verified_title">Не перевірено</string>
- <string name="user_id_info_not_verified_text">Ця сутність ще не перевірена. Ви не можете переконатися, чи сутність справді відповідає вказаній особі.</string>
+ <string name="user_id_info_certified_title">Перевірено</string>
+ <string name="user_id_info_certified_text">Не перевірено</string>
+ <string name="user_id_info_uncertified_title">Не перевірено</string>
+ <string name="user_id_info_uncertified_text">Ця сутність ще не перевірена. Ви не можете переконатися, чи сутність справді відповідає вказаній особі.</string>
<string name="user_id_info_invalid_title">Недійсна</string>
<string name="user_id_info_invalid_text">Щось неправильне у цій сутності!</string>
<!--Edit key-->
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 9f5d3f535..847dfffb5 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -29,6 +29,7 @@
<string name="title_encrypt_to_file">"Encrypt To File"</string>
<string name="title_decrypt_to_file">"Decrypt To File"</string>
<string name="title_import_keys">"Import Keys"</string>
+ <string name="title_add_keys">"Add Keys"</string>
<string name="title_export_key">"Export Key"</string>
<string name="title_export_keys">"Export Keys"</string>
<string name="title_key_not_found">"Key Not Found"</string>
@@ -46,10 +47,11 @@
<string name="section_general">"General"</string>
<string name="section_defaults">"Defaults"</string>
<string name="section_advanced">"Advanced"</string>
+ <string name="section_certify">"Certify"</string>
<string name="section_actions">"Actions"</string>
<string name="section_share_key">"Whole key"</string>
<string name="section_certification_key">"Your Key used for certification"</string>
- <string name="section_upload_key">"Upload Key"</string>
+ <string name="section_upload_key">"Synchronize Key"</string>
<string name="section_key_server">"Keyserver"</string>
<string name="section_fingerprint">"Fingerprint"</string>
<string name="section_key_to_certify">"Key to be certified"</string>
@@ -133,7 +135,7 @@
<string name="label_name">"Name"</string>
<string name="label_comment">"Comment"</string>
<string name="label_email">"Email"</string>
- <string name="label_send_key">"Upload key to selected keyserver after certification"</string>
+ <string name="label_send_key">"Synchronize with public keyservers"</string>
<string name="label_fingerprint">"Fingerprint"</string>
<string name="expiry_date_dialog_title">"Set expiry date"</string>
<string name="label_first_keyserver_is_used">"(First keyserver listed is preferred)"</string>
@@ -234,6 +236,7 @@
<string name="select_key_to_certify">"Please select a key to be used for certification!"</string>
<string name="key_too_big_for_sharing">"Key is too big to be shared this way!"</string>
<string name="text_copied_to_clipboard">"Text has been copied to the clipboard!"</string>
+ <string name="select_key_for_exchange">"Please select a key to be used for secure exchange!"</string>
<!--
errors
@@ -259,28 +262,31 @@
<string name="error_jelly_bean_needed">"You need Android 4.1 to use Android's NFC Beam feature!"</string>
<string name="error_nfc_needed">"NFC is not available on your device!"</string>
<string name="error_nothing_import">"No keys found!"</string>
- <string name="error_query_too_short">"Search query too short. Please refine your query!"</string>
- <string name="error_searching_keys">"An error occurred when searching for keys."</string>
- <string name="error_too_many_responses">"Key search query returned too many candidates. Please refine your query!"</string>
- <string name="error_too_short_or_too_many_responses">"Either no keys or too many have been found. Please improve your query!"</string>
<string name="error_contacts_key_id_missing">"Retrieving the key ID from contacts failed!"</string>
-
- <string name="error_import_no_valid_keys">"No valid keys found in File/Clipboard!"</string>
<string name="error_generic_report_bug">"A generic error occurred, please create a new bug report for OpenKeychain."</string>
- <plurals name="error_import_non_pgp_part">
- <item quantity="one">"part of the loaded file is a valid OpenPGP object but not a OpenPGP key"</item>
- <item quantity="other">"parts of the loaded file are valid OpenPGP objects but not OpenPGP keys"</item>
- </plurals>
<!-- results shown after decryption/verification -->
+ <string name="decrypt_result_no_signature">"Not Signed"</string>
<string name="decrypt_result_invalid_signature">"Invalid signature!"</string>
- <string name="decrypt_result_signature_unknown_pub_key">"Unknown public key"</string>
- <string name="decrypt_result_signature_uncertified">"Valid signature (uncertified)"</string>
- <string name="decrypt_result_signature_certified">"Valid signature (certified)"</string>
- <string name="decrypt_result_decrypted">"Successfully decrypted"</string>
- <string name="decrypt_result_decrypted_unknown_pub_key">"Successfully decrypted but unknown public key"</string>
- <string name="decrypt_result_decrypted_and_signature_uncertified">"Successfully decrypted and valid signature (uncertified)"</string>
- <string name="decrypt_result_decrypted_and_signature_certified">"Successfully decrypted and valid signature (certified)"</string>
+ <string name="decrypt_result_signature_uncertified">"Signed by (not certified!)"</string>
+ <string name="decrypt_result_signature_certified">"Signed by"</string>
+ <string name="decrypt_result_signature_expired_key">"Key is expired!"</string>
+ <string name="decrypt_result_signature_revoked_key">"Key has been revoked!"</string>
+ <string name="decrypt_result_signature_missing_key">"Unknown public key"</string>
+ <string name="decrypt_result_encrypted">"Encrypted"</string>
+ <string name="decrypt_result_not_encrypted">"Not Encrypted"</string>
+ <string name="decrypt_result_action_show">"Show"</string>
+ <string name="decrypt_result_action_Lookup">"Lookup"</string>
+ <string name="decrypt_invalid_text">"Either the signature is invalid or the key has been revoked/is expired. You can not be sure who wrote the text. Do you still want to display it?"</string>
+ <string name="decrypt_invalid_button">"I understand the risks, display it!"</string>
+
+ <!-- Add keys -->
+ <string name="add_keys_section_secure_exchange">"Exchange"</string>
+ <string name="add_keys_section_secure_add">"Add"</string>
+ <string name="add_keys_my_key">"My key:"</string>
+ <string name="add_keys_start_exchange">"Start exchange"</string>
+ <string name="add_keys_qr_code">"Scan QR Code"</string>
+ <string name="add_keys_nfc">"Receive via NFC"</string>
<!-- progress dialogs, usually ending in '…' -->
<string name="progress_done">"Done."</string>
@@ -387,6 +393,11 @@
<string name="import_qr_code_too_short_fingerprint">"Fingerprint is too short (&lt; 16 characters)"</string>
<string name="import_qr_code_button">"Scan QR Code…"</string>
+ <!-- Generic result toast -->
+ <string name="view_log">"View Log"</string>
+ <string name="with_warnings">", with warnings"</string>
+ <string name="with_cancelled">", until cancelled"</string>
+
<!-- Import result toast -->
<plurals name="import_keys_added_and_updated_1">
<item quantity="one">"Successfully imported key"</item>
@@ -408,15 +419,26 @@
<item quantity="one">"Import failed for one key!"</item>
<item quantity="other">"Import failed for %d keys!"</item>
</plurals>
- <string name="view_log">"View Log"</string>
- <string name="import_error_nothing">"Nothing to import."</string>
- <string name="import_error_nothing_cancelled">"Import cancelled."</string>
- <string name="import_with_warnings">", with warnings"</string>
- <string name="import_with_cancelled">", until cancelled"</string>
<plurals name="import_error">
<item quantity="one">"Import failed!"</item>
<item quantity="other">"Import of %d keys failed!"</item>
</plurals>
+ <string name="import_error_nothing">"Nothing to import."</string>
+ <string name="import_error_nothing_cancelled">"Import cancelled."</string>
+
+ <!-- Certify result toast -->
+ <plurals name="certify_keys_ok">
+ <item quantity="one">"Successfully certified key%2$s."</item>
+ <item quantity="other">"Successfully certified %1$d keys%2$s."</item>
+ </plurals>
+ <plurals name="certify_keys_with_errors">
+ <item quantity="one">"Certification failed!"</item>
+ <item quantity="other">"Certification failed for %d keys!"</item>
+ </plurals>
+ <plurals name="certify_error">
+ <item quantity="one">"Certification failed!"</item>
+ <item quantity="other">"Certification of %d keys failed!"</item>
+ </plurals>
<!-- Intent labels -->
<string name="intent_decrypt_file">"Decrypt File with OpenKeychain"</string>
@@ -472,6 +494,8 @@
<string name="key_list_empty_text3">"or"</string>
<string name="key_list_empty_button_create">"creating your own key"</string>
<string name="key_list_empty_button_import">"importing an existing key."</string>
+ <string name="key_list_filter_show_all">"Show all keys"</string>
+ <string name="key_list_filter_show_certified">"Show certified keys only"</string>
<!-- Key view -->
<string name="key_view_action_edit">"Edit key"</string>
@@ -488,10 +512,10 @@
<string name="key_view_tab_certs">"Certificates"</string>
<string name="user_id_info_revoked_title">"Revoked"</string>
<string name="user_id_info_revoked_text">"This identity has been revoked by the key owner. It is no longer valid."</string>
- <string name="user_id_info_verified_title">"Verified"</string>
- <string name="user_id_info_verified_text">"This identity has been verified."</string>
- <string name="user_id_info_not_verified_title">"Not verified"</string>
- <string name="user_id_info_not_verified_text">"This identity has not been verified yet. You cannot be sure if the identity really corresponds to a specific person."</string>
+ <string name="user_id_info_certified_title">"Certified"</string>
+ <string name="user_id_info_certified_text">"This identity has been certified by you."</string>
+ <string name="user_id_info_uncertified_title">"Not certified"</string>
+ <string name="user_id_info_uncertified_text">"This identity has not been certified yet. You cannot be sure if the identity really corresponds to a specific person."</string>
<string name="user_id_info_invalid_title">"Invalid"</string>
<string name="user_id_info_invalid_text">"Something is wrong with this identity!"</string>
@@ -769,7 +793,7 @@
<string name="msg_mf_subkey_strip">"Stripping subkey %s"</string>
<string name="msg_mf_success">"Keyring successfully modified"</string>
<string name="msg_mf_uid_add">"Adding user id %s"</string>
- <string name="msg_mf_uid_primary">"Changing primary uid to %s"</string>
+ <string name="msg_mf_uid_primary">"Changing primary user id to %s"</string>
<string name="msg_mf_uid_revoke">"Revoking user id %s"</string>
<string name="msg_mf_uid_error_empty">"User ID must not be empty!"</string>
<string name="msg_mf_unlock_error">"Error unlocking keyring!"</string>
@@ -875,11 +899,42 @@
<string name="msg_se">"Starting sign and/or encrypt operation"</string>
<string name="msg_se_symmetric">"Preparing symmetric encryption"</string>
- <string name="msg_crt_upload_success">"Successfully uploaded key to server"</string>
+ <string name="msg_crt_certifying">"Generating certifications"</string>
+ <string name="msg_crt_certify_all">"Certifying all user ids for key %s"</string>
+ <plurals name="msg_crt_certify_some">
+ <item quantity="one">"Certifying one user id for key %2$s"</item>
+ <item quantity="other">"Certifying %1$d user ids for key %2$s"</item>
+ </plurals>
+ <string name="msg_crt_error_master_not_found">"Master key not found!"</string>
+ <string name="msg_crt_error_nothing">"No keys certified!"</string>
+ <string name="msg_crt_error_unlock">"Error unlocking master key!"</string>
+ <string name="msg_crt">"Certifying keyrings"</string>
+ <string name="msg_crt_master_fetch">"Fetching certifying master key"</string>
+ <string name="msg_crt_save">"Saving certified key %s"</string>
+ <string name="msg_crt_saving">"Saving keyrings"</string>
+ <string name="msg_crt_unlock">"Unlocking master key"</string>
<string name="msg_crt_success">"Successfully certified identities"</string>
+ <string name="msg_crt_warn_not_found">"Key not found!"</string>
+ <string name="msg_crt_warn_cert_failed">"Certificate generation failed!"</string>
+ <string name="msg_crt_warn_save_failed">"Save operation failed!"</string>
+
+ <string name="msg_crt_upload_success">"Successfully uploaded key to server"</string>
<string name="msg_acc_saved">"Account saved"</string>
+ <string name="msg_download_success">"Downloaded successfully!"</string>
+ <string name="msg_download_no_valid_keys">"No valid keys found in File/Clipboard!"</string>
+ <string name="msg_download_no_pgp_parts">"TODO: plurals!"</string>
+ <plurals name="error_import_non_pgp_part">
+ <item quantity="one">"part of the loaded file is a valid OpenPGP object but not a OpenPGP key"</item>
+ <item quantity="other">"parts of the loaded file are valid OpenPGP objects but not OpenPGP keys"</item>
+ </plurals>
+ <string name="msg_download_query_too_short">"Search query too short. Please refine your query!"</string>
+ <string name="msg_download_too_many_responses">"Key search query returned too many candidates. Please refine your query!"</string>
+ <string name="msg_download_query_too_short_or_too_many_responses">"Either no keys or too many have been found. Please improve your query!"</string>
+
+ <string name="msg_download_query_failed">"An error occurred when searching for keys."</string>
+
<!-- PassphraseCache -->
<string name="passp_cache_notif_click_to_clear">"Click to clear cached passphrases"</string>
<string name="passp_cache_notif_n_keys">"OpenKeychain has cached %d passphrases"</string>
@@ -894,7 +949,8 @@
<string name="unknown_uid">"&lt;unknown&gt;"</string>
<string name="empty_certs">"No certificates for this key"</string>
<string name="certs_text">"Only validated self-certificates and validated certificates created with your keys are displayed here."</string>
- <string name="section_uids_to_certify">"Identities to certify"</string>
+ <string name="section_uids_to_certify">"Identities"</string>
+ <string name="certify_text">"Do the selected identities match the persons you are exchanging keys with? Deselect all unknown ones."</string>
<string name="label_revocation">"Revocation Reason"</string>
<string name="label_verify_status">"Verification Status"</string>
<string name="label_cert_type">"Type"</string>
@@ -914,6 +970,7 @@
<string name="swipe_to_update">"Swipe down to update from keyserver"</string>
<string name="error_no_file_selected">"Select at least one file to encrypt!"</string>
<string name="error_multi_not_supported">"Saving of multiple files not supported. This is a limitation on current Android."</string>
+ <string name="key_colon">"Key:"</string>
<!-- First Time -->
<string name="first_time_text1">"Take back your privacy with OpenKeychain!"</string>