aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/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
49 files changed, 2789 insertions, 687 deletions
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);
}