aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain')
-rw-r--r--OpenKeychain/src/main/AndroidManifest.xml3
-rw-r--r--OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java76
-rw-r--r--OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java278
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java89
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java188
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java88
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java84
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java112
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java317
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesInputFragment.java143
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesListFragment.java623
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java104
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java97
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java145
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java104
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java21
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java83
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java127
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java163
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java10
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_files_fragment.xml149
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_files_input_fragment.xml83
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_files_list_fragment.xml52
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_list_entry.xml243
-rw-r--r--OpenKeychain/src/main/res/menu/decrypt_item_context_menu.xml22
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml3
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java10
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java12
50 files changed, 2171 insertions, 1480 deletions
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index 89bdc49ac..75d94ae69 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -717,6 +717,9 @@
android:exported="false"
android:process=":remote_api" />
<service
+ android:name=".service.KeychainNewService"
+ android:exported="false" />
+ <service
android:name=".service.KeychainService"
android:exported="false" />
diff --git a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java
new file mode 100644
index 000000000..d35f1d751
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann
+ *
+ * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details.
+ */
+
+package org.spongycastle.openpgp.operator.jcajce;
+
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
+import org.spongycastle.openpgp.operator.PGPDataDecryptor;
+import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactory
+{
+ private final PublicKeyDataDecryptorFactory mWrappedDecryptor;
+ private final Map<ByteBuffer, byte[]> mSessionKeyCache;
+
+ private OperatorHelper mOperatorHelper;
+
+ public CachingDataDecryptorFactory(String providerName,
+ final Map<ByteBuffer,byte[]> sessionKeyCache)
+ {
+ mWrappedDecryptor = null;
+ mSessionKeyCache = sessionKeyCache;
+
+ mOperatorHelper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+ }
+
+ public CachingDataDecryptorFactory(PublicKeyDataDecryptorFactory wrapped,
+ final Map<ByteBuffer,byte[]> sessionKeyCache)
+ {
+ mWrappedDecryptor = wrapped;
+ mSessionKeyCache = sessionKeyCache;
+
+ }
+
+ public boolean hasCachedSessionData(PGPPublicKeyEncryptedData encData) throws PGPException {
+ ByteBuffer bi = ByteBuffer.wrap(encData.getSessionKey()[0]);
+ return mSessionKeyCache.containsKey(bi);
+ }
+
+ public Map<ByteBuffer, byte[]> getCachedSessionKeys() {
+ return mSessionKeyCache;
+ }
+
+ public boolean canDecrypt() {
+ return mWrappedDecryptor != null;
+ }
+
+ @Override
+ public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) throws PGPException {
+ ByteBuffer bi = ByteBuffer.wrap(secKeyData[0]); // encoded MPI
+ if (mSessionKeyCache.containsKey(bi)) {
+ return mSessionKeyCache.get(bi);
+ }
+
+ byte[] sessionData = mWrappedDecryptor.recoverSessionData(keyAlgorithm, secKeyData);
+ mSessionKeyCache.put(bi, sessionData);
+ return sessionData;
+ }
+
+ @Override
+ public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
+ throws PGPException {
+ if (mWrappedDecryptor != null) {
+ return mWrappedDecryptor.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
+ }
+ return mOperatorHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java
deleted file mode 100644
index 067bb3e19..000000000
--- a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/**
- * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann
- *
- * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details.
- */
-
-package org.spongycastle.openpgp.operator.jcajce;
-
-import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
-import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
-import org.spongycastle.jcajce.util.NamedJcaJceHelper;
-import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.operator.PGPDataDecryptor;
-import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
-
-import java.nio.ByteBuffer;
-import java.security.Provider;
-import java.util.Map;
-
-
-/**
- * This class is based on JcePublicKeyDataDecryptorFactoryBuilder
- *
- */
-public class NfcSyncPublicKeyDataDecryptorFactoryBuilder
-{
- private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
- private OperatorHelper contentHelper = new OperatorHelper(new DefaultJcaJceHelper());
- private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter();
-// private JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder();
-// private JcaKeyFingerprintCalculator fingerprintCalculator = new JcaKeyFingerprintCalculator();
-
- public static class NfcInteractionNeeded extends RuntimeException
- {
- public byte[] encryptedSessionKey;
-
- public NfcInteractionNeeded(byte[] encryptedSessionKey)
- {
- super("NFC interaction required!");
- this.encryptedSessionKey = encryptedSessionKey;
- }
- }
-
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder()
- {
- }
-
- /**
- * Set the provider object to use for creating cryptographic primitives in the resulting factory the builder produces.
- *
- * @param provider provider object for cryptographic primitives.
- * @return the current builder.
- */
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder setProvider(Provider provider)
- {
- this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
- keyConverter.setProvider(provider);
- this.contentHelper = helper;
-
- return this;
- }
-
- /**
- * Set the provider name to use for creating cryptographic primitives in the resulting factory the builder produces.
- *
- * @param providerName the name of the provider to reference for cryptographic primitives.
- * @return the current builder.
- */
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder setProvider(String providerName)
- {
- this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
- keyConverter.setProvider(providerName);
- this.contentHelper = helper;
-
- return this;
- }
-
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder setContentProvider(Provider provider)
- {
- this.contentHelper = new OperatorHelper(new ProviderJcaJceHelper(provider));
-
- return this;
- }
-
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder setContentProvider(String providerName)
- {
- this.contentHelper = new OperatorHelper(new NamedJcaJceHelper(providerName));
-
- return this;
- }
-
- public PublicKeyDataDecryptorFactory build(final Map<ByteBuffer,byte[]> nfcDecryptedMap) {
- return new PublicKeyDataDecryptorFactory()
- {
- public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
- throws PGPException
- {
- if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
- {
- throw new PGPException("ECDH not supported!");
- }
-
- return decryptSessionData(keyAlgorithm, secKeyData, nfcDecryptedMap);
- }
-
- public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
- throws PGPException
- {
- return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
- }
- };
- }
-
-// public PublicKeyDataDecryptorFactory build(final PrivateKey privKey)
-// {
-// return new PublicKeyDataDecryptorFactory()
-// {
-// public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
-// throws PGPException
-// {
-// if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
-// {
-// throw new PGPException("ECDH requires use of PGPPrivateKey for decryption");
-// }
-// return decryptSessionData(keyAlgorithm, privKey, secKeyData);
-// }
-//
-// public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
-// throws PGPException
-// {
-// return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
-// }
-// };
-// }
-
-// public PublicKeyDataDecryptorFactory build(final PGPPrivateKey privKey, final byte[] nfcDecrypted)
-// {
-// return new PublicKeyDataDecryptorFactory()
-// {
-// public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
-// throws PGPException
-// {
-// if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
-// {
-// return decryptSessionData(privKey.getPrivateKeyDataPacket(), privKey.getPublicKeyPacket(), secKeyData);
-// }
-//
-// return decryptSessionData(keyAlgorithm, keyConverter.getPrivateKey(privKey), secKeyData, nfcDecrypted);
-// }
-//
-// public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
-// throws PGPException
-// {
-// return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
-// }
-// };
-// }
-
-// private byte[] decryptSessionData(BCPGKey privateKeyPacket, PublicKeyPacket pubKeyData, byte[][] secKeyData)
-// throws PGPException
-// {
-// ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKeyData.getKey();
-// X9ECParameters x9Params = NISTNamedCurves.getByOID(ecKey.getCurveOID());
-//
-// byte[] enc = secKeyData[0];
-//
-// int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
-// byte[] pEnc = new byte[pLen];
-//
-// System.arraycopy(enc, 2, pEnc, 0, pLen);
-//
-// byte[] keyEnc = new byte[enc[pLen + 2]];
-//
-// System.arraycopy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.length);
-//
-// Cipher c = helper.createKeyWrapper(ecKey.getSymmetricKeyAlgorithm());
-//
-// ECPoint S = x9Params.getCurve().decodePoint(pEnc).multiply(((ECSecretBCPGKey)privateKeyPacket).getX()).normalize();
-//
-// RFC6637KDFCalculator rfc6637KDFCalculator = new RFC6637KDFCalculator(digestCalculatorProviderBuilder.build().get(ecKey.getHashAlgorithm()), ecKey.getSymmetricKeyAlgorithm());
-// Key key = new SecretKeySpec(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, fingerprintCalculator.calculateFingerprint(pubKeyData)), "AESWrap");
-//
-// try
-// {
-// c.init(Cipher.UNWRAP_MODE, key);
-//
-// Key paddedSessionKey = c.unwrap(keyEnc, "Session", Cipher.SECRET_KEY);
-//
-// return PGPPad.unpadSessionData(paddedSessionKey.getEncoded());
-// }
-// catch (InvalidKeyException e)
-// {
-// throw new PGPException("error setting asymmetric cipher", e);
-// }
-// catch (NoSuchAlgorithmException e)
-// {
-// throw new PGPException("error setting asymmetric cipher", e);
-// }
-// }
-
- private byte[] decryptSessionData(int keyAlgorithm, byte[][] secKeyData,
- Map<ByteBuffer,byte[]> nfcDecryptedMap)
- throws PGPException
- {
-// Cipher c1 = helper.createPublicKeyCipher(keyAlgorithm);
-//
-// try
-// {
-// c1.init(Cipher.DECRYPT_MODE, privKey);
-// }
-// catch (InvalidKeyException e)
-// {
-// throw new PGPException("error setting asymmetric cipher", e);
-// }
-
- if (keyAlgorithm == PGPPublicKey.RSA_ENCRYPT
- || keyAlgorithm == PGPPublicKey.RSA_GENERAL)
- {
- ByteBuffer bi = ByteBuffer.wrap(secKeyData[0]); // encoded MPI
-
- if (nfcDecryptedMap.containsKey(bi)) {
- return nfcDecryptedMap.get(bi);
- } else {
- // catch this when decryptSessionData() is executed and divert digest to card,
- // when doing the operation again reuse nfcDecrypted
- throw new NfcInteractionNeeded(bi.array());
- }
-
-// c1.update(bi, 2, bi.length - 2);
- }
- else
- {
- throw new PGPException("ElGamal not supported!");
-
-// ElGamalKey k = (ElGamalKey)privKey;
-// int size = (k.getParameters().getP().bitLength() + 7) / 8;
-// byte[] tmp = new byte[size];
-//
-// byte[] bi = secKeyData[0]; // encoded MPI
-// if (bi.length - 2 > size) // leading Zero? Shouldn't happen but...
-// {
-// c1.update(bi, 3, bi.length - 3);
-// }
-// else
-// {
-// System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2);
-// c1.update(tmp);
-// }
-//
-// bi = secKeyData[1]; // encoded MPI
-// for (int i = 0; i != tmp.length; i++)
-// {
-// tmp[i] = 0;
-// }
-//
-// if (bi.length - 2 > size) // leading Zero? Shouldn't happen but...
-// {
-// c1.update(bi, 3, bi.length - 3);
-// }
-// else
-// {
-// System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2);
-// c1.update(tmp);
-// }
- }
-
-// try
-// {
-// return c1.doFinal();
-// }
-// catch (Exception e)
-// {
-// throw new PGPException("exception decrypting session data", e);
-// }
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java
index 5a3321ac8..fae59b7a4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java
@@ -18,17 +18,20 @@
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
+import android.os.Parcelable;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.concurrent.atomic.AtomicBoolean;
-public abstract class BaseOperation implements PassphraseCacheInterface {
+public abstract class BaseOperation <T extends Parcelable> implements PassphraseCacheInterface {
final public Context mContext;
final public Progressable mProgressable;
@@ -73,6 +76,10 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
mCancelled = cancelled;
}
+ public OperationResult execute(T input, CryptoInputParcel cryptoInput) {
+ return null;
+ }
+
public void updateProgress(int message, int current, int total) {
if (mProgressable != null) {
mProgressable.setProgress(message, current, total);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
index 186d0531d..439260b74 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
@@ -64,7 +64,7 @@ public class CertifyOperation extends BaseOperation {
super(context, providerHelper, progressable, cancelled);
}
- public CertifyResult certify(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput, String keyServerUri) {
+ public CertifyResult execute(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput) {
OperationLog log = new OperationLog();
log.add(LogType.MSG_CRT, 0);
@@ -186,8 +186,8 @@ public class CertifyOperation extends BaseOperation {
HkpKeyserver keyServer = null;
ImportExportOperation importExportOperation = null;
- if (keyServerUri != null) {
- keyServer = new HkpKeyserver(keyServerUri);
+ if (parcel.keyServerUri != null) {
+ keyServer = new HkpKeyserver(parcel.keyServerUri);
importExportOperation = new ImportExportOperation(mContext, mProviderHelper, mProgressable);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
index e8e888c7a..469e386cb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
@@ -37,7 +37,6 @@ import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -51,7 +50,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @see SaveKeyringParcel
*
*/
-public class EditKeyOperation extends BaseOperation {
+public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> {
public EditKeyOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
index 651d15e8f..7c58d62f8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
@@ -55,7 +55,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* a pending result, it will terminate.
*
*/
-public class SignEncryptOperation extends BaseOperation {
+public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
public SignEncryptOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
index ac571390a..25a86f137 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
@@ -22,6 +22,7 @@ import android.os.Parcel;
import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
@@ -36,6 +37,8 @@ public class DecryptVerifyResult extends InputPendingResult {
// https://tools.ietf.org/html/rfc4880#page56
String mCharset;
+ CryptoInputParcel mCachedCryptoInputParcel;
+
byte[] mOutputBytes;
public DecryptVerifyResult(int result, OperationLog log) {
@@ -50,6 +53,7 @@ public class DecryptVerifyResult extends InputPendingResult {
super(source);
mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader());
+ mCachedCryptoInputParcel = source.readParcelable(CryptoInputParcel.class.getClassLoader());
}
@@ -65,6 +69,14 @@ public class DecryptVerifyResult extends InputPendingResult {
mSignatureResult = signatureResult;
}
+ public CryptoInputParcel getCachedCryptoInputParcel() {
+ return mCachedCryptoInputParcel;
+ }
+
+ public void setCachedCryptoInputParcel(CryptoInputParcel cachedCryptoInputParcel) {
+ mCachedCryptoInputParcel = cachedCryptoInputParcel;
+ }
+
public OpenPgpMetadata getDecryptMetadata() {
return mDecryptMetadata;
}
@@ -97,6 +109,7 @@ public class DecryptVerifyResult extends InputPendingResult {
super.writeToParcel(dest, flags);
dest.writeParcelable(mSignatureResult, 0);
dest.writeParcelable(mDecryptMetadata, 0);
+ dest.writeParcelable(mCachedCryptoInputParcel, 0);
}
public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() {
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 17d342341..31a3925da 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -21,23 +21,18 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
-import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
-import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
-import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
@@ -51,7 +46,6 @@ import java.security.interfaces.RSAPrivateCrtKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
@@ -270,19 +264,20 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
}
}
- public PublicKeyDataDecryptorFactory getDecryptorFactory(CryptoInputParcel cryptoInput) {
+ public CachingDataDecryptorFactory getCachingDecryptorFactory(CryptoInputParcel cryptoInput) {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
- return new NfcSyncPublicKeyDataDecryptorFactoryBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- cryptoInput.getCryptoData()
- );
+ return new CachingDataDecryptorFactory(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME,
+ cryptoInput.getCryptoData());
} else {
- return new JcePublicKeyDataDecryptorFactoryBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
+ return new CachingDataDecryptorFactory(
+ new JcePublicKeyDataDecryptorFactoryBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey),
+ cryptoInput.getCryptoData());
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index 4382c9fae..4651f12d4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -40,11 +40,10 @@ import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
-import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
-import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.BaseOperation;
@@ -76,10 +75,7 @@ import java.security.SignatureException;
import java.util.Date;
import java.util.Iterator;
-/**
- * This class uses a Builder pattern!
- */
-public class PgpDecryptVerify extends BaseOperation {
+public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel> {
public PgpDecryptVerify(Context context, ProviderHelper providerHelper, Progressable progressable) {
super(context, providerHelper, progressable);
@@ -541,24 +537,33 @@ public class PgpDecryptVerify extends BaseOperation {
currentProgress += 2;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
- try {
- PublicKeyDataDecryptorFactory decryptorFactory
- = secretEncryptionKey.getDecryptorFactory(cryptoInput);
- try {
- clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
- } catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) {
- log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
- }
+ CachingDataDecryptorFactory decryptorFactory
+ = secretEncryptionKey.getCachingDecryptorFactory(cryptoInput);
+
+ // special case: if the decryptor does not have a session key cached for this encrypted
+ // data, and can't actually decrypt on its own, return a pending intent
+ if (!decryptorFactory.canDecrypt()
+ && !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) {
- symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
- } catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
return new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
secretEncryptionKey.getRing().getMasterKeyId(),
- secretEncryptionKey.getKeyId(), e.encryptedSessionKey
+ secretEncryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
));
+
+ }
+
+ try {
+ clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
+ } catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) {
+ log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
+
+ symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
+
+ cryptoInput.addCryptoData(decryptorFactory.getCachedSessionKeys());
+
encryptedData = encryptedDataAsymmetric;
} else {
// there wasn't even any useful data
@@ -665,9 +670,6 @@ public class PgpDecryptVerify extends BaseOperation {
PGPLiteralData literalData = (PGPLiteralData) dataChunk;
- // reported size may be null if partial packets are involved (highly unlikely though)
- Long originalSize = literalData.getDataLengthIfAvailable();
-
String originalFilename = literalData.getFileName();
String mimeType = null;
if (literalData.getFormat() == PGPLiteralData.TEXT
@@ -690,12 +692,6 @@ public class PgpDecryptVerify extends BaseOperation {
}
}
- metadata = new OpenPgpMetadata(
- originalFilename,
- mimeType,
- literalData.getModificationTime().getTime(),
- originalSize == null ? 0 : originalSize);
-
if (!"".equals(originalFilename)) {
log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename);
}
@@ -703,15 +699,26 @@ public class PgpDecryptVerify extends BaseOperation {
mimeType);
log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1,
new Date(literalData.getModificationTime().getTime()).toString());
- if (originalSize != null) {
- log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1,
- Long.toString(originalSize));
- } else {
- log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1);
- }
// return here if we want to decrypt the metadata only
if (input.isDecryptMetadataOnly()) {
+
+ // this operation skips the entire stream to find the data length!
+ Long originalSize = literalData.findDataLength();
+
+ if (originalSize != null) {
+ log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1,
+ Long.toString(originalSize));
+ } else {
+ log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1);
+ }
+
+ metadata = new OpenPgpMetadata(
+ originalFilename,
+ mimeType,
+ literalData.getModificationTime().getTime(),
+ originalSize == null ? 0 : originalSize);
+
log.add(LogType.MSG_DC_OK_META_ONLY, indent);
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
@@ -760,6 +767,21 @@ public class PgpDecryptVerify extends BaseOperation {
// TODO: slow annealing to fake a progress?
}
+ // after going through the stream, size should be available
+ Long originalSize = literalData.getDataLengthIfAvailable();
+ if (originalSize != null) {
+ log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1,
+ Long.toString(originalSize));
+ } else {
+ log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1);
+ }
+
+ metadata = new OpenPgpMetadata(
+ originalFilename,
+ mimeType,
+ literalData.getModificationTime().getTime(),
+ originalSize == null ? 0 : originalSize);
+
if (signature != null) {
updateProgress(R.string.progress_verifying_signature, 90, 100);
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
@@ -821,6 +843,7 @@ public class PgpDecryptVerify extends BaseOperation {
// Return a positive result, with metadata and verification info
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ result.setCachedCryptoInputParcel(cryptoInput);
result.setDecryptMetadata(metadata);
result.setSignatureResult(signatureResultBuilder.build());
result.setCharset(charset);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
index fd3c4910c..fa6268758 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
@@ -20,13 +20,8 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
-import java.nio.ByteBuffer;
-import java.util.Date;
-import java.util.Map;
-
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
index 2000a6525..74f7c6513 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
@@ -57,6 +57,11 @@ public class TemporaryStorageProvider extends ContentProvider {
return context.getContentResolver().insert(BASE_URI, contentValues);
}
+ public static Uri createFile(Context context) {
+ ContentValues contentValues = new ContentValues();
+ return context.getContentResolver().insert(BASE_URI, contentValues);
+ }
+
public static int cleanUp(Context context) {
return context.getContentResolver().delete(BASE_URI, COLUMN_TIME + "< ?",
new String[]{Long.toString(System.currentTimeMillis() - Constants.TEMPFILE_TTL)});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
index 8721f4c0c..a11f81658 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
@@ -43,6 +43,8 @@ public class CertifyActionsParcel implements Parcelable {
public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
+ public String keyServerUri;
+
public CertifyActionsParcel(long masterKeyId) {
mMasterKeyId = masterKeyId;
mLevel = CertifyLevel.DEFAULT;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java
new file mode 100644
index 000000000..9e33a1421
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.operations.BaseOperation;
+import org.sufficientlysecure.keychain.operations.CertifyOperation;
+import org.sufficientlysecure.keychain.operations.EditKeyOperation;
+import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
+import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.util.Log;
+
+/**
+ * This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
+ * data from the activities or other apps, executes them, and stops itself after doing them.
+ */
+public class KeychainNewService extends Service implements Progressable {
+
+ // messenger for communication (hack)
+ public static final String EXTRA_MESSENGER = "messenger";
+
+ // extras for operation
+ public static final String EXTRA_OPERATION_INPUT = "op_input";
+ public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
+
+ // this attribute can possibly merged with the one above? not sure...
+ private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
+
+ ThreadLocal<Messenger> mMessenger = new ThreadLocal<>();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * This is run on the main thread, we need to spawn a runnable which runs on another thread for the actual operation
+ */
+ @Override
+ public int onStartCommand(final Intent intent, int flags, int startId) {
+
+ Runnable actionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // We have not been cancelled! (yet)
+ mActionCanceled.set(false);
+
+ Bundle extras = intent.getExtras();
+
+ // Set messenger for communication (for this particular thread)
+ mMessenger.set(extras.<Messenger>getParcelable(EXTRA_MESSENGER));
+
+ // Input
+ Parcelable inputParcel = extras.getParcelable(EXTRA_OPERATION_INPUT);
+ CryptoInputParcel cryptoInput = extras.getParcelable(EXTRA_CRYPTO_INPUT);
+
+ // Operation
+ BaseOperation op;
+
+ // just for brevity
+ KeychainNewService outerThis = KeychainNewService.this;
+ if (inputParcel instanceof SignEncryptParcel) {
+ op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
+ } else if (inputParcel instanceof PgpDecryptVerifyInputParcel) {
+ op = new PgpDecryptVerify(outerThis, new ProviderHelper(outerThis), outerThis);
+ } else if (inputParcel instanceof SaveKeyringParcel) {
+ op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
+ } else if (inputParcel instanceof CertifyAction) {
+ op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
+ } else {
+ return;
+ }
+
+ @SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
+ OperationResult result = op.execute(inputParcel, cryptoInput);
+
+ sendMessageToHandler(MessageStatus.OKAY, result);
+
+ }
+ };
+
+ Thread actionThread = new Thread(actionRunnable);
+ actionThread.start();
+
+ return START_NOT_STICKY;
+ }
+
+ private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
+
+ Message msg = Message.obtain();
+ assert msg != null;
+ msg.arg1 = status.ordinal();
+ if (arg2 != null) {
+ msg.arg2 = arg2;
+ }
+ if (data != null) {
+ msg.setData(data);
+ }
+
+ try {
+ mMessenger.get().send(msg);
+ } catch (RemoteException e) {
+ Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
+ } catch (NullPointerException e) {
+ Log.w(Constants.TAG, "Messenger is null!", e);
+ }
+ }
+
+ private void sendMessageToHandler(MessageStatus status, OperationResult data) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
+ sendMessageToHandler(status, null, bundle);
+ }
+
+ private void sendMessageToHandler(MessageStatus status) {
+ sendMessageToHandler(status, null, null);
+ }
+
+ /**
+ * Set progress of ProgressDialog by sending message to handler on UI thread
+ */
+ @Override
+ public void setProgress(String message, int progress, int max) {
+ Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
+ + max);
+
+ Bundle data = new Bundle();
+ if (message != null) {
+ data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
+ }
+ data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
+ data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
+
+ sendMessageToHandler(MessageStatus.UPDATE_PROGRESS, null, data);
+ }
+
+ @Override
+ public void setProgress(int resourceId, int progress, int max) {
+ setProgress(getString(resourceId), progress, max);
+ }
+
+ @Override
+ public void setProgress(int progress, int max) {
+ setProgress(null, progress, max);
+ }
+
+ @Override
+ public void setPreventCancel() {
+ sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
index b8cb9de27..ba877c2a2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
@@ -96,14 +96,9 @@ public class KeychainService extends Service implements Progressable {
public static final String EXTRA_DATA = "data";
/* possible actions */
- public static final String ACTION_SIGN_ENCRYPT = Constants.INTENT_PREFIX + "SIGN_ENCRYPT";
-
- public static final String ACTION_DECRYPT_VERIFY = Constants.INTENT_PREFIX + "DECRYPT_VERIFY";
public static final String ACTION_VERIFY_KEYBASE_PROOF = Constants.INTENT_PREFIX + "VERIFY_KEYBASE_PROOF";
- public static final String ACTION_DECRYPT_METADATA = Constants.INTENT_PREFIX + "DECRYPT_METADATA";
-
public static final String ACTION_EDIT_KEYRING = Constants.INTENT_PREFIX + "EDIT_KEYRING";
public static final String ACTION_PROMOTE_KEYRING = Constants.INTENT_PREFIX + "PROMOTE_KEYRING";
@@ -113,8 +108,6 @@ public class KeychainService extends Service implements Progressable {
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
- public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
-
public static final String ACTION_DELETE = Constants.INTENT_PREFIX + "DELETE";
public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE";
@@ -123,14 +116,6 @@ public class KeychainService extends Service implements Progressable {
/* keys for data bundle */
- // encrypt
- public static final String ENCRYPT_DECRYPT_INPUT_URI = "input_uri";
- public static final String ENCRYPT_DECRYPT_OUTPUT_URI = "output_uri";
- public static final String SIGN_ENCRYPT_PARCEL = "sign_encrypt_parcel";
-
- // decrypt/verify
- public static final String DECRYPT_VERIFY_PARCEL = "decrypt_verify_parcel";
-
// keybase proof
public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint";
public static final String KEYBASE_PROOF = "keybase_proof";
@@ -158,9 +143,6 @@ public class KeychainService extends Service implements Progressable {
// upload key
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
- // certify key
- public static final String CERTIFY_PARCEL = "certify_parcel";
-
// promote key
public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id";
public static final String PROMOTE_CARD_AID = "promote_card_aid";
@@ -232,23 +214,6 @@ public class KeychainService extends Service implements Progressable {
// executeServiceMethod action from extra bundle
switch (action) {
- case ACTION_CERTIFY_KEYRING: {
-
- // Input
- CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL);
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
- String keyServerUri = data.getString(UPLOAD_KEY_SERVER);
-
- // Operation
- CertifyOperation op = new CertifyOperation(mKeychainService, providerHelper, mKeychainService,
- mActionCanceled);
- CertifyResult result = op.certify(parcel, cryptoInput, keyServerUri);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
case ACTION_CONSOLIDATE: {
// Operation
@@ -264,24 +229,6 @@ public class KeychainService extends Service implements Progressable {
break;
}
- case ACTION_DECRYPT_METADATA: {
-
- // Input
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
- PgpDecryptVerifyInputParcel input = data.getParcelable(DECRYPT_VERIFY_PARCEL);
-
- // this action is here for compatibility only
- input.setDecryptMetadataOnly(true);
-
- // Operation
- PgpDecryptVerify op = new PgpDecryptVerify(mKeychainService, providerHelper, mKeychainService);
- DecryptVerifyResult decryptVerifyResult = op.execute(input, cryptoInput);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, decryptVerifyResult);
-
- break;
- }
case ACTION_VERIFY_KEYBASE_PROOF: {
try {
@@ -376,25 +323,6 @@ public class KeychainService extends Service implements Progressable {
break;
}
- case ACTION_DECRYPT_VERIFY: {
-
- // Input
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
- PgpDecryptVerifyInputParcel input = data.getParcelable(DECRYPT_VERIFY_PARCEL);
-
- // for compatibility
- // TODO merge with ACTION_DECRYPT_METADATA
- input.setDecryptMetadataOnly(false);
-
- // Operation
- PgpDecryptVerify op = new PgpDecryptVerify(mKeychainService, providerHelper, mKeychainService);
- DecryptVerifyResult decryptVerifyResult = op.execute(input, cryptoInput);
-
- // Output
- sendMessageToHandler(MessageStatus.OKAY, decryptVerifyResult);
-
- break;
- }
case ACTION_DELETE: {
// Input
@@ -495,22 +423,6 @@ public class KeychainService extends Service implements Progressable {
break;
}
- case ACTION_SIGN_ENCRYPT: {
-
- // Input
- SignEncryptParcel inputParcel = data.getParcelable(SIGN_ENCRYPT_PARCEL);
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- // Operation
- SignEncryptOperation op = new SignEncryptOperation(
- mKeychainService, providerHelper, mKeychainService, mActionCanceled);
- SignEncryptResult result = op.execute(inputParcel, cryptoInput);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
case ACTION_UPLOAD_KEYRING: {
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
index a0bb250d4..8b90e41bf 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
@@ -17,8 +17,7 @@
package org.sufficientlysecure.keychain.service;
-import android.app.Activity;
-import android.content.Intent;
+import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -27,7 +26,6 @@ import android.support.v4.app.FragmentManager;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
@@ -65,68 +63,58 @@ public class ServiceProgressHandler extends Handler {
public static final String KEYBASE_PRESENCE_URL = "keybase_presence_url";
public static final String KEYBASE_PRESENCE_LABEL = "keybase_presence_label";
- Activity mActivity;
- ProgressDialogFragment mProgressDialogFragment;
+ FragmentActivity mActivity;
- public ServiceProgressHandler(Activity activity) {
- this.mActivity = activity;
+ public ServiceProgressHandler(FragmentActivity activity) {
+ mActivity = activity;
}
- public ServiceProgressHandler(Activity activity, ProgressDialogFragment progressDialogFragment) {
- this.mActivity = activity;
- this.mProgressDialogFragment = progressDialogFragment;
+ public void showProgressDialog() {
+ showProgressDialog("", ProgressDialog.STYLE_SPINNER, false);
}
- public ServiceProgressHandler(Activity activity, String progressDialogMessage, int progressDialogStyle) {
- this(activity, progressDialogMessage, progressDialogStyle, false);
- }
+ public void showProgressDialog(
+ String progressDialogMessage, int progressDialogStyle, boolean cancelable) {
- public ServiceProgressHandler(Activity activity,
- String progressDialogMessage,
- int progressDialogStyle,
- boolean cancelable) {
- this.mActivity = activity;
- this.mProgressDialogFragment = ProgressDialogFragment.newInstance(
+ final ProgressDialogFragment frag = ProgressDialogFragment.newInstance(
progressDialogMessage,
progressDialogStyle,
cancelable);
- }
-
- public void showProgressDialog(FragmentActivity activity) {
- if (mProgressDialogFragment == null) {
- return;
- }
// TODO: This is a hack!, see
// http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
- final FragmentManager manager = activity.getSupportFragmentManager();
+ final FragmentManager manager = mActivity.getSupportFragmentManager();
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
- mProgressDialogFragment.show(manager, "progressDialog");
+ frag.show(manager, "progressDialog");
}
});
+
}
@Override
public void handleMessage(Message message) {
Bundle data = message.getData();
- if (mProgressDialogFragment == null) {
- // Log.e(Constants.TAG,
- // "Progress has not been updated because mProgressDialogFragment was null!");
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) mActivity.getSupportFragmentManager()
+ .findFragmentByTag("progressDialog");
+
+ if (progressDialogFragment == null) {
+ Log.e(Constants.TAG, "Progress has not been updated because mProgressDialogFragment was null!");
return;
}
MessageStatus status = MessageStatus.fromInt(message.arg1);
switch (status) {
case OKAY:
- mProgressDialogFragment.dismissAllowingStateLoss();
+ progressDialogFragment.dismissAllowingStateLoss();
break;
case EXCEPTION:
- mProgressDialogFragment.dismissAllowingStateLoss();
+ progressDialogFragment.dismissAllowingStateLoss();
// show error from service
if (data.containsKey(DATA_ERROR)) {
@@ -140,23 +128,24 @@ public class ServiceProgressHandler extends Handler {
case UPDATE_PROGRESS:
if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) {
+ String msg = null;
+ int progress = data.getInt(DATA_PROGRESS), max =data.getInt(DATA_PROGRESS_MAX);
+
// update progress from service
if (data.containsKey(DATA_MESSAGE)) {
- mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE),
- data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
+ msg = data.getString(DATA_MESSAGE);
} else if (data.containsKey(DATA_MESSAGE_ID)) {
- mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID),
- data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
- } else {
- mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS),
- data.getInt(DATA_PROGRESS_MAX));
+ msg = mActivity.getString(data.getInt(DATA_MESSAGE_ID));
}
+
+ onSetProgress(msg, progress, max);
+
}
break;
case PREVENT_CANCEL:
- mProgressDialogFragment.setPreventCancel(true);
+ progressDialogFragment.setPreventCancel(true);
break;
default:
@@ -164,4 +153,19 @@ public class ServiceProgressHandler extends Handler {
break;
}
}
+
+ protected void onSetProgress(String msg, int progress, int max) {
+
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) mActivity.getSupportFragmentManager()
+ .findFragmentByTag("progressDialog");
+
+ if (msg != null) {
+ progressDialogFragment.setProgress(msg, progress, max);
+ } else {
+ progressDialogFragment.setProgress(progress, max);
+ }
+
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
index 3d1ccaca1..ee7caf2d8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
@@ -97,8 +97,12 @@ public class CryptoInputParcel implements Parcelable {
mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
}
+ public void addCryptoData(Map<ByteBuffer, byte[]> cachedSessionKeys) {
+ mCryptoData.putAll(cachedSessionKeys);
+ }
+
public Map<ByteBuffer, byte[]> getCryptoData() {
- return Collections.unmodifiableMap(mCryptoData);
+ return mCryptoData;
}
public Date getSignatureTime() {
@@ -138,4 +142,5 @@ public class CryptoInputParcel implements Parcelable {
b.append("}");
return b.toString();
}
+
}
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 6afe2256b..e39a3a0bf 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
@@ -64,7 +64,8 @@ import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
-public class CertifyKeyFragment extends CachingCryptoOperationFragment<CertifyActionsParcel>
+public class CertifyKeyFragment
+ extends CachingCryptoOperationFragment<CertifyActionsParcel, CertifyResult>
implements LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_CHECK_STATES = "check_states";
@@ -89,7 +90,6 @@ public class CertifyKeyFragment extends CachingCryptoOperationFragment<CertifyAc
private static final int INDEX_IS_REVOKED = 4;
private MultiUserIdsAdapter mUserIdsAdapter;
- private Messenger mPassthroughMessenger;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
@@ -102,10 +102,6 @@ public class CertifyKeyFragment extends CachingCryptoOperationFragment<CertifyAc
return;
}
- mPassthroughMessenger = getActivity().getIntent().getParcelableExtra(
- KeychainService.EXTRA_MESSENGER);
- mPassthroughMessenger = null; // TODO doesn't work with CryptoOperationFragment, disabled for now
-
ArrayList<Boolean> checkedStates;
if (savedInstanceState != null) {
checkedStates = (ArrayList<Boolean>) savedInstanceState.getSerializable(ARG_CHECK_STATES);
@@ -306,97 +302,39 @@ public class CertifyKeyFragment extends CachingCryptoOperationFragment<CertifyAc
}
@Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput, CertifyActionsParcel actionsParcel) {
- Bundle data = new Bundle();
- {
-
- if (actionsParcel == null) {
- // Bail out if there is not at least one user id selected
- ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
- if (certifyActions.isEmpty()) {
- Notify.create(getActivity(), "No identities selected!",
- Notify.Style.ERROR).show();
- return;
- }
-
- long selectedKeyId = mCertifyKeySpinner.getSelectedKeyId();
-
- // fill values for this action
- actionsParcel = new CertifyActionsParcel(selectedKeyId);
- actionsParcel.mCertifyActions.addAll(certifyActions);
-
- // cached for next cryptoOperation loop
- cacheActionsParcel(actionsParcel);
- }
-
- data.putParcelable(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
- data.putParcelable(KeychainService.CERTIFY_PARCEL, actionsParcel);
-
- if (mUploadKeyCheckbox.isChecked()) {
- String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
- data.putString(KeychainService.UPLOAD_KEY_SERVER, keyserver);
- }
+ protected CertifyActionsParcel createOperationInput() {
+
+ // Bail out if there is not at least one user id selected
+ ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
+ if (certifyActions.isEmpty()) {
+ Notify.create(getActivity(), "No identities selected!",
+ Notify.Style.ERROR).show();
+ return null;
}
- // Send all information needed to service to sign key in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
- intent.setAction(KeychainService.ACTION_CERTIFY_KEYRING);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
+ long selectedKeyId = mCertifyKeySpinner.getSelectedKeyId();
- if (mPassthroughMessenger != null) {
- intent.putExtra(KeychainService.EXTRA_MESSENGER, mPassthroughMessenger);
- } else {
+ // fill values for this action
+ CertifyActionsParcel actionsParcel = new CertifyActionsParcel(selectedKeyId);
+ actionsParcel.mCertifyActions.addAll(certifyActions);
- // Message is received after signing is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_certifying),
- ProgressDialog.STYLE_SPINNER,
- true
- ) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by KeychainIntentCryptoServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
+ // cached for next cryptoOperation loop
+ cacheActionsParcel(actionsParcel);
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- Bundle data = message.getData();
-
- CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
-
- Intent intent = new Intent();
- intent.putExtra(CertifyResult.EXTRA_RESULT, result);
- getActivity().setResult(Activity.RESULT_OK, intent);
- getActivity().finish();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
- }
-
- // start service with intent
- getActivity().startService(intent);
-
- if (mPassthroughMessenger != null) {
- getActivity().setResult(Activity.RESULT_OK);
- getActivity().finish();
- }
+ return actionsParcel;
+ }
+ @Override
+ protected void onCryptoOperationSuccess(CertifyResult result) {
+ Intent intent = new Intent();
+ intent.putExtra(CertifyResult.EXTRA_RESULT, result);
+ getActivity().setResult(Activity.RESULT_OK, intent);
+ getActivity().finish();
}
@Override
protected void onCryptoOperationCancelled() {
super.onCryptoOperationCancelled();
}
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
index 054e43f21..6a9bb7b11 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
@@ -49,11 +49,7 @@ public class ConsolidateDialogActivity extends FragmentActivity {
private void consolidateRecovery(boolean recovery) {
// Message is received after importing is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- this,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -94,7 +90,10 @@ public class ConsolidateDialogActivity extends FragmentActivity {
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- saveHandler.showProgressDialog(this);
+ saveHandler.showProgressDialog(
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL, false
+ );
// start service with intent
startService(intent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
index b39064e20..ebbd01afe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -197,11 +197,7 @@ public class CreateKeyFinalFragment extends Fragment {
Intent intent = new Intent(getActivity(), KeychainService.class);
intent.setAction(KeychainService.ACTION_EDIT_KEYRING);
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_building_key),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -245,7 +241,8 @@ public class CreateKeyFinalFragment extends Fragment {
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
- saveHandler.showProgressDialog(getActivity());
+ saveHandler.showProgressDialog(getString(R.string.progress_building_key),
+ ProgressDialog.STYLE_HORIZONTAL, false);
getActivity().startService(intent);
}
@@ -271,11 +268,7 @@ public class CreateKeyFinalFragment extends Fragment {
intent.putExtra(KeychainService.EXTRA_DATA, data);
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_uploading),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -301,10 +294,13 @@ public class CreateKeyFinalFragment extends Fragment {
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- saveHandler.showProgressDialog(getActivity());
+ saveHandler.showProgressDialog(
+ getString(R.string.progress_uploading),
+ ProgressDialog.STYLE_HORIZONTAL, false);
// start service with intent
getActivity().startService(intent);
+
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java
index ba8e8321a..2ab8c5967 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java
@@ -176,11 +176,7 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
public void importKey() {
// Message is received after decrypting is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -243,7 +239,10 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
- saveHandler.showProgressDialog(getActivity());
+ saveHandler.showProgressDialog(
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL, false
+ );
// start service with intent
getActivity().startService(intent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java
index 81fb6a392..b56c38d19 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java
@@ -17,11 +17,12 @@
package org.sufficientlysecure.keychain.ui;
+import java.util.ArrayList;
+
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.view.View;
import android.widget.Toast;
@@ -29,7 +30,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.intents.OpenKeychainIntents;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
-import org.sufficientlysecure.keychain.util.Log;
+
public class DecryptFilesActivity extends BaseActivity {
@@ -94,13 +95,26 @@ public class DecryptFilesActivity extends BaseActivity {
}
boolean showOpenDialog = ACTION_DECRYPT_DATA_OPEN.equals(action);
- DecryptFilesFragment frag = DecryptFilesFragment.newInstance(uri, showOpenDialog);
+ DecryptFilesInputFragment frag = DecryptFilesInputFragment.newInstance(uri, showOpenDialog);
// Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
getSupportFragmentManager().beginTransaction()
.replace(R.id.decrypt_files_fragment_container, frag)
- .commitAllowingStateLoss();
+ .commit();
+
+ }
+
+ public void displayListFragment(Uri inputUri) {
+
+ ArrayList<Uri> uris = new ArrayList<>();
+ uris.add(inputUri);
+ DecryptFilesListFragment frag = DecryptFilesListFragment.newInstance(uris);
+
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.decrypt_files_fragment_container, frag)
+ .addToBackStack("list")
+ .commit();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java
deleted file mode 100644
index b2d9596fd..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.TextView;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.ui.util.Notify;
-import org.sufficientlysecure.keychain.util.FileHelper;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.io.File;
-
-public class DecryptFilesFragment extends DecryptFragment {
- public static final String ARG_URI = "uri";
- public static final String ARG_OPEN_DIRECTLY = "open_directly";
-
- private static final int REQUEST_CODE_INPUT = 0x00007003;
- private static final int REQUEST_CODE_OUTPUT = 0x00007007;
-
- // view
- private TextView mFilename;
- private CheckBox mDeleteAfter;
- private View mDecryptButton;
-
- // model
- private Uri mInputUri = null;
- private Uri mOutputUri = null;
-
- private String mCurrentCryptoOperation;
-
- /**
- * Creates new instance of this fragment
- */
- public static DecryptFilesFragment newInstance(Uri uri, boolean openDirectly) {
- DecryptFilesFragment frag = new DecryptFilesFragment();
-
- Bundle args = new Bundle();
- args.putParcelable(ARG_URI, uri);
- args.putBoolean(ARG_OPEN_DIRECTLY, openDirectly);
-
- frag.setArguments(args);
-
- return frag;
- }
-
- /**
- * Inflate the layout for this fragment
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.decrypt_files_fragment, container, false);
-
- mFilename = (TextView) view.findViewById(R.id.decrypt_files_filename);
- mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_files_delete_after_decryption);
- mDecryptButton = view.findViewById(R.id.decrypt_files_action_decrypt);
- view.findViewById(R.id.decrypt_files_browse).setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT);
- } else {
- FileHelper.openFile(DecryptFilesFragment.this, mInputUri, "*/*",
- REQUEST_CODE_INPUT);
- }
- }
- });
- mDecryptButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- decryptAction();
- }
- });
-
- return view;
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- outState.putParcelable(ARG_URI, mInputUri);
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- Bundle state = savedInstanceState != null ? savedInstanceState : getArguments();
- setInputUri(state.<Uri>getParcelable(ARG_URI));
-
- // should only come from args
- if (state.getBoolean(ARG_OPEN_DIRECTLY, false)) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT);
- } else {
- FileHelper.openFile(DecryptFilesFragment.this, mInputUri, "*/*", REQUEST_CODE_INPUT);
- }
- }
- }
-
- private void setInputUri(Uri inputUri) {
- if (inputUri == null) {
- mInputUri = null;
- mFilename.setText("");
- return;
- }
-
- mInputUri = inputUri;
- mFilename.setText(FileHelper.getFilename(getActivity(), mInputUri));
- }
-
- private void decryptAction() {
- if (mInputUri == null) {
- Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show();
- return;
- }
-
- startDecryptFilenames();
- }
-
- private String removeEncryptedAppend(String name) {
- if (name.endsWith(Constants.FILE_EXTENSION_ASC)
- || name.endsWith(Constants.FILE_EXTENSION_PGP_MAIN)
- || name.endsWith(Constants.FILE_EXTENSION_PGP_ALTERNATE)) {
- return name.substring(0, name.length() - 4);
- }
- return name;
- }
-
- private void askForOutputFilename(String originalFilename) {
- if (TextUtils.isEmpty(originalFilename)) {
- originalFilename = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri));
- }
-
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- File file = new File(mInputUri.getPath());
- File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
- File targetFile = new File(parentDir, originalFilename);
- FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file),
- getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT);
- } else {
- FileHelper.saveDocument(this, "*/*", originalFilename, REQUEST_CODE_OUTPUT);
- }
- }
-
- private void startDecrypt() {
- mCurrentCryptoOperation = KeychainService.ACTION_DECRYPT_VERIFY;
- cryptoOperation(new CryptoInputParcel());
- }
-
- private void startDecryptFilenames() {
- mCurrentCryptoOperation = KeychainService.ACTION_DECRYPT_METADATA;
- cryptoOperation(new CryptoInputParcel());
- }
-
- @Override
- @SuppressLint("HandlerLeak")
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
- // use current operation, either decrypt metadata or decrypt payload
- intent.setAction(mCurrentCryptoOperation);
-
- // data
-
- Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri);
-
- PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(mInputUri, mOutputUri)
- .setAllowSymmetricDecryption(true);
-
- data.putParcelable(KeychainService.DECRYPT_VERIFY_PARCEL, input);
- data.putParcelable(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
-
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Message is received after decrypting is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_decrypting),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- DecryptVerifyResult pgpResult =
- returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
-
- if (pgpResult.success()) {
- switch (mCurrentCryptoOperation) {
- case KeychainService.ACTION_DECRYPT_METADATA: {
- askForOutputFilename(pgpResult.getDecryptMetadata().getFilename());
- break;
- }
- case KeychainService.ACTION_DECRYPT_VERIFY: {
- // display signature result in activity
- loadVerifyResult(pgpResult);
-
- if (mDeleteAfter.isChecked()) {
- // Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);
- deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
- setInputUri(null);
- }
-
- /*
- // A future open after decryption feature
- if () {
- Intent viewFile = new Intent(Intent.ACTION_VIEW);
- viewFile.setInputData(mOutputUri);
- startActivity(viewFile);
- }
- */
- break;
- }
- default: {
- Log.e(Constants.TAG, "Bug: not supported operation!");
- break;
- }
- }
- }
- pgpResult.createNotify(getActivity()).show(DecryptFilesFragment.this);
- }
-
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_INPUT: {
- if (resultCode == Activity.RESULT_OK && data != null) {
- setInputUri(data.getData());
- }
- return;
- }
-
- case REQUEST_CODE_OUTPUT: {
- // This happens after output file was selected, so start our operation
- if (resultCode == Activity.RESULT_OK && data != null) {
- mOutputUri = data.getData();
- startDecrypt();
- }
- return;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
- }
-
- @Override
- protected void onVerifyLoaded(boolean hideErrorOverlay) {
-
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesInputFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesInputFragment.java
new file mode 100644
index 000000000..be00183b2
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesInputFragment.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.util.FileHelper;
+
+public class DecryptFilesInputFragment extends Fragment {
+ public static final String ARG_URI = "uri";
+ public static final String ARG_OPEN_DIRECTLY = "open_directly";
+
+ private static final int REQUEST_CODE_INPUT = 0x00007003;
+
+ private TextView mFilename;
+ private View mDecryptButton;
+
+ private Uri mInputUri = null;
+
+ public static DecryptFilesInputFragment newInstance(Uri uri, boolean openDirectly) {
+ DecryptFilesInputFragment frag = new DecryptFilesInputFragment();
+
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_URI, uri);
+ args.putBoolean(ARG_OPEN_DIRECTLY, openDirectly);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.decrypt_files_input_fragment, container, false);
+
+ // hide result view for this fragment
+ getActivity().findViewById(R.id.result_main_layout).setVisibility(View.GONE);
+
+ mFilename = (TextView) view.findViewById(R.id.decrypt_files_filename);
+ mDecryptButton = view.findViewById(R.id.decrypt_files_action_decrypt);
+ view.findViewById(R.id.decrypt_files_browse).setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ FileHelper.openDocument(DecryptFilesInputFragment.this, "*/*", REQUEST_CODE_INPUT);
+ } else {
+ FileHelper.openFile(DecryptFilesInputFragment.this, mInputUri, "*/*",
+ REQUEST_CODE_INPUT);
+ }
+ }
+ });
+ mDecryptButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ decryptAction();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putParcelable(ARG_URI, mInputUri);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Bundle state = savedInstanceState != null ? savedInstanceState : getArguments();
+ setInputUri(state.<Uri>getParcelable(ARG_URI));
+
+ // should only come from args
+ if (state.getBoolean(ARG_OPEN_DIRECTLY, false)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ FileHelper.openDocument(DecryptFilesInputFragment.this, "*/*", REQUEST_CODE_INPUT);
+ } else {
+ FileHelper.openFile(DecryptFilesInputFragment.this, mInputUri, "*/*", REQUEST_CODE_INPUT);
+ }
+ }
+ }
+
+ private void setInputUri(Uri inputUri) {
+ if (inputUri == null) {
+ mInputUri = null;
+ mFilename.setText("");
+ return;
+ }
+
+ mInputUri = inputUri;
+ mFilename.setText(FileHelper.getFilename(getActivity(), mInputUri));
+ }
+
+ private void decryptAction() {
+ if (mInputUri == null) {
+ Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show();
+ return;
+ }
+
+ DecryptFilesActivity activity = (DecryptFilesActivity) getActivity();
+ activity.displayListFragment(mInputUri);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode != REQUEST_CODE_INPUT) {
+ return;
+ }
+
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ setInputUri(data.getData());
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesListFragment.java
new file mode 100644
index 000000000..d3b52fe78
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesListFragment.java
@@ -0,0 +1,623 @@
+/*
+ * 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 java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.widget.DefaultItemAnimator;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.PopupMenu;
+import android.widget.PopupMenu.OnDismissListener;
+import android.widget.PopupMenu.OnMenuItemClickListener;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.ViewAnimator;
+
+import org.openintents.openpgp.OpenPgpMetadata;
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
+// this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15)
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder;
+import org.sufficientlysecure.keychain.ui.DecryptFilesListFragment.DecryptFilesAdapter.ViewModel;
+import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+import org.sufficientlysecure.keychain.util.FileHelper;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class DecryptFilesListFragment
+ extends CryptoOperationFragment<PgpDecryptVerifyInputParcel,DecryptVerifyResult>
+ implements OnMenuItemClickListener {
+ public static final String ARG_URIS = "uris";
+
+ private static final int REQUEST_CODE_OUTPUT = 0x00007007;
+
+ private ArrayList<Uri> mInputUris;
+ private HashMap<Uri, Uri> mOutputUris;
+ private ArrayList<Uri> mPendingInputUris;
+
+ private Uri mCurrentInputUri;
+
+ private DecryptFilesAdapter mAdapter;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static DecryptFilesListFragment newInstance(ArrayList<Uri> uris) {
+ DecryptFilesListFragment frag = new DecryptFilesListFragment();
+
+ Bundle args = new Bundle();
+ args.putParcelableArrayList(ARG_URIS, uris);
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.decrypt_files_list_fragment, container, false);
+
+ RecyclerView vFilesList = (RecyclerView) view.findViewById(R.id.decrypted_files_list);
+
+ vFilesList.addItemDecoration(new SpacesItemDecoration(
+ FormattingUtils.dpToPx(getActivity(), 4)));
+ vFilesList.setHasFixedSize(true);
+ vFilesList.setLayoutManager(new LinearLayoutManager(getActivity()));
+ vFilesList.setItemAnimator(new DefaultItemAnimator());
+
+ mAdapter = new DecryptFilesAdapter(getActivity(), this);
+ vFilesList.setAdapter(mAdapter);
+
+ return view;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putParcelableArrayList(ARG_URIS, mInputUris);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ displayInputUris(getArguments().<Uri>getParcelableArrayList(ARG_URIS));
+ }
+
+ private String removeEncryptedAppend(String name) {
+ if (name.endsWith(Constants.FILE_EXTENSION_ASC)
+ || name.endsWith(Constants.FILE_EXTENSION_PGP_MAIN)
+ || name.endsWith(Constants.FILE_EXTENSION_PGP_ALTERNATE)) {
+ return name.substring(0, name.length() - 4);
+ }
+ return name;
+ }
+
+ private void askForOutputFilename(Uri inputUri, String originalFilename, String mimeType) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ File file = new File(inputUri.getPath());
+ File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
+ File targetFile = new File(parentDir, originalFilename);
+ FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file),
+ getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT);
+ } else {
+ FileHelper.saveDocument(this, mimeType, originalFilename, REQUEST_CODE_OUTPUT);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_OUTPUT: {
+ // This happens after output file was selected, so start our operation
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ Uri saveUri = data.getData();
+ Uri outputUri = mOutputUris.get(mCurrentInputUri);
+ // TODO save from outputUri to saveUri
+
+ mCurrentInputUri = null;
+ }
+ return;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+ }
+
+ private void displayInputUris(ArrayList<Uri> uris) {
+ mInputUris = uris;
+ mOutputUris = new HashMap<>(uris.size());
+ for (Uri uri : uris) {
+ mAdapter.add(uri);
+ mOutputUris.put(uri, TemporaryStorageProvider.createFile(getActivity()));
+ }
+
+ mPendingInputUris = uris;
+
+ cryptoOperation();
+ }
+
+ @Override
+ protected boolean onCryptoSetProgress(String msg, int progress, int max) {
+ mAdapter.setProgress(mCurrentInputUri, progress, max, msg);
+ return true;
+ }
+
+ @Override
+ protected void dismissProgress() {
+ // progress shown inline, so never mind
+ }
+
+ @Override
+ protected void onCryptoOperationError(DecryptVerifyResult result) {
+ final Uri uri = mCurrentInputUri;
+ mCurrentInputUri = null;
+
+ mAdapter.addResult(uri, result, null, null, null);
+ }
+
+ @Override
+ protected void onCryptoOperationSuccess(DecryptVerifyResult result) {
+ final Uri uri = mCurrentInputUri;
+ mCurrentInputUri = null;
+
+ Drawable icon = null;
+ OnClickListener onFileClick = null, onKeyClick = null;
+
+ if (result.getDecryptMetadata() != null && result.getDecryptMetadata().getMimeType() != null) {
+ icon = loadIcon(result.getDecryptMetadata().getMimeType());
+ }
+
+ OpenPgpSignatureResult sigResult = result.getSignatureResult();
+ if (sigResult != null) {
+ final long keyId = sigResult.getKeyId();
+ if (sigResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) {
+ onKeyClick = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ Intent intent = new Intent(activity, ViewKeyActivity.class);
+ intent.setData(KeyRings.buildUnifiedKeyRingUri(keyId));
+ activity.startActivity(intent);
+ }
+ };
+ }
+ }
+
+ if (result.success() && result.getDecryptMetadata() != null) {
+ final OpenPgpMetadata metadata = result.getDecryptMetadata();
+ onFileClick = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Activity activity = getActivity();
+ if (activity == null || mCurrentInputUri != null) {
+ return;
+ }
+
+ Uri outputUri = mOutputUris.get(uri);
+ Intent intent = new Intent();
+ intent.setDataAndType(outputUri, metadata.getMimeType());
+ activity.startActivity(intent);
+ }
+ };
+ }
+
+ mAdapter.addResult(uri, result, icon, onFileClick, onKeyClick);
+
+ }
+
+ @Override
+ protected PgpDecryptVerifyInputParcel createOperationInput() {
+
+ if (mCurrentInputUri == null) {
+ if (mPendingInputUris.isEmpty()) {
+ // nothing left to do
+ return null;
+ }
+
+ mCurrentInputUri = mPendingInputUris.remove(0);
+ }
+
+ Uri currentOutputUri = mOutputUris.get(mCurrentInputUri);
+ Log.d(Constants.TAG, "mInputUri=" + mCurrentInputUri + ", mOutputUri=" + currentOutputUri);
+
+ return new PgpDecryptVerifyInputParcel(mCurrentInputUri, currentOutputUri)
+ .setAllowSymmetricDecryption(true);
+
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ if (mAdapter.mMenuClickedModel == null || !mAdapter.mMenuClickedModel.hasResult()) {
+ return false;
+ }
+ Activity activity = getActivity();
+ if (activity == null) {
+ return false;
+ }
+
+ ViewModel model = mAdapter.mMenuClickedModel;
+ DecryptVerifyResult result = model.mResult;
+ switch (menuItem.getItemId()) {
+ case R.id.view_log:
+ Intent intent = new Intent(activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result);
+ activity.startActivity(intent);
+ return true;
+ case R.id.decrypt_save:
+ OpenPgpMetadata metadata = result.getDecryptMetadata();
+ if (metadata == null) {
+ return true;
+ }
+ mCurrentInputUri = model.mInputUri;
+ askForOutputFilename(model.mInputUri, metadata.getFilename(), metadata.getMimeType());
+ return true;
+ case R.id.decrypt_delete:
+ Notify.create(activity, "decrypt/delete not yet implemented", Style.ERROR).show(this);
+ return true;
+ }
+ return false;
+ }
+
+ public static class DecryptFilesAdapter extends RecyclerView.Adapter<ViewHolder> {
+ private Context mContext;
+ private ArrayList<ViewModel> mDataset;
+ private OnMenuItemClickListener mMenuItemClickListener;
+ private ViewModel mMenuClickedModel;
+
+ public class ViewModel {
+ Context mContext;
+ Uri mInputUri;
+ DecryptVerifyResult mResult;
+ Drawable mIcon;
+
+ OnClickListener mOnFileClickListener;
+ OnClickListener mOnKeyClickListener;
+
+ int mProgress, mMax;
+ String mProgressMsg;
+
+ ViewModel(Context context, Uri uri) {
+ mContext = context;
+ mInputUri = uri;
+ mProgress = 0;
+ mMax = 100;
+ }
+
+ void addResult(DecryptVerifyResult result) {
+ mResult = result;
+ }
+
+ void addIcon(Drawable icon) {
+ mIcon = icon;
+ }
+
+ void setOnClickListeners(OnClickListener onFileClick, OnClickListener onKeyClick) {
+ mOnFileClickListener = onFileClick;
+ mOnKeyClickListener = onKeyClick;
+ }
+
+ boolean hasResult() {
+ return mResult != null;
+ }
+
+ void setProgress(int progress, int max, String msg) {
+ if (msg != null) {
+ mProgressMsg = msg;
+ }
+ mProgress = progress;
+ mMax = max;
+ }
+
+ // Depends on inputUri only
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ViewModel viewModel = (ViewModel) o;
+ return !(mResult != null ? !mResult.equals(viewModel.mResult)
+ : viewModel.mResult != null);
+ }
+
+ // Depends on inputUri only
+ @Override
+ public int hashCode() {
+ return mResult != null ? mResult.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return mResult.toString();
+ }
+ }
+
+ // Provide a suitable constructor (depends on the kind of dataset)
+ public DecryptFilesAdapter(Context context, OnMenuItemClickListener menuItemClickListener) {
+ mContext = context;
+ mMenuItemClickListener = menuItemClickListener;
+ mDataset = new ArrayList<>();
+ }
+
+ // Create new views (invoked by the layout manager)
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ //inflate your layout and pass it to view holder
+ View v = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.decrypt_list_entry, parent, false);
+ return new ViewHolder(v);
+ }
+
+ // Replace the contents of a view (invoked by the layout manager)
+ @Override
+ public void onBindViewHolder(ViewHolder holder, final int position) {
+ // - get element from your dataset at this position
+ // - replace the contents of the view with that element
+ final ViewModel model = mDataset.get(position);
+
+ if (model.hasResult()) {
+ if (holder.vAnimator.getDisplayedChild() != 1) {
+ holder.vAnimator.setDisplayedChild(1);
+ }
+
+ KeyFormattingUtils.setStatus(mContext, holder, model.mResult);
+
+ OpenPgpMetadata metadata = model.mResult.getDecryptMetadata();
+ holder.vFilename.setText(metadata.getFilename());
+
+ long size = metadata.getOriginalSize();
+ if (size == -1 || size == 0) {
+ holder.vFilesize.setText("");
+ } else {
+ holder.vFilesize.setText(FileHelper.readableFileSize(size));
+ }
+
+ // TODO thumbnail from OpenPgpMetadata
+ if (model.mIcon != null) {
+ holder.vThumbnail.setImageDrawable(model.mIcon);
+ } else {
+ holder.vThumbnail.setImageResource(R.drawable.ic_doc_generic_am);
+ }
+
+ holder.vFile.setOnClickListener(model.mOnFileClickListener);
+ holder.vSignatureLayout.setOnClickListener(model.mOnKeyClickListener);
+
+ holder.vContextMenu.setTag(model);
+ holder.vContextMenu.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mMenuClickedModel = model;
+ PopupMenu menu = new PopupMenu(mContext, view);
+ menu.inflate(R.menu.decrypt_item_context_menu);
+ menu.setOnMenuItemClickListener(mMenuItemClickListener);
+ menu.setOnDismissListener(new OnDismissListener() {
+ @Override
+ public void onDismiss(PopupMenu popupMenu) {
+ mMenuClickedModel = null;
+ }
+ });
+ menu.show();
+ }
+ });
+
+ } else {
+ if (holder.vAnimator.getDisplayedChild() != 0) {
+ holder.vAnimator.setDisplayedChild(0);
+ }
+
+ holder.vProgress.setProgress(model.mProgress);
+ holder.vProgress.setMax(model.mMax);
+ holder.vProgressMsg.setText(model.mProgressMsg);
+ }
+
+ }
+
+ // Return the size of your dataset (invoked by the layout manager)
+ @Override
+ public int getItemCount() {
+ return mDataset.size();
+ }
+
+ public void add(Uri uri) {
+ ViewModel newModel = new ViewModel(mContext, uri);
+ mDataset.add(newModel);
+ notifyItemInserted(mDataset.size());
+ }
+
+ public void setProgress(Uri uri, int progress, int max, String msg) {
+ ViewModel newModel = new ViewModel(mContext, uri);
+ int pos = mDataset.indexOf(newModel);
+ mDataset.get(pos).setProgress(progress, max, msg);
+ notifyItemChanged(pos);
+ }
+
+ public void addResult(Uri uri, DecryptVerifyResult result, Drawable icon,
+ OnClickListener onFileClick, OnClickListener onKeyClick) {
+
+ ViewModel model = new ViewModel(mContext, uri);
+ int pos = mDataset.indexOf(model);
+ model = mDataset.get(pos);
+
+ model.addResult(result);
+ if (icon != null) {
+ model.addIcon(icon);
+ }
+ model.setOnClickListeners(onFileClick, onKeyClick);
+
+ notifyItemChanged(pos);
+ }
+
+ }
+
+
+ // Provide a reference to the views for each data item
+ // Complex data items may need more than one view per item, and
+ // you provide access to all the views for a data item in a view holder
+ public static class ViewHolder extends RecyclerView.ViewHolder implements StatusHolder {
+ public ViewAnimator vAnimator;
+
+ public ProgressBar vProgress;
+ public TextView vProgressMsg;
+
+ public View vFile;
+ public TextView vFilename;
+ public TextView vFilesize;
+ public ImageView vThumbnail;
+
+ public ImageView vEncStatusIcon;
+ public TextView vEncStatusText;
+
+ public ImageView vSigStatusIcon;
+ public TextView vSigStatusText;
+ public View vSignatureLayout;
+ public TextView vSignatureName;
+ public TextView vSignatureMail;
+ public TextView vSignatureAction;
+
+ public View vContextMenu;
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+
+ vAnimator = (ViewAnimator) itemView.findViewById(R.id.view_animator);
+
+ vProgress = (ProgressBar) itemView.findViewById(R.id.progress);
+ vProgressMsg = (TextView) itemView.findViewById(R.id.progress_msg);
+
+ vFile = itemView.findViewById(R.id.file);
+ vFilename = (TextView) itemView.findViewById(R.id.filename);
+ vFilesize = (TextView) itemView.findViewById(R.id.filesize);
+ vThumbnail = (ImageView) itemView.findViewById(R.id.thumbnail);
+
+ vEncStatusIcon = (ImageView) itemView.findViewById(R.id.result_encryption_icon);
+ vEncStatusText = (TextView) itemView.findViewById(R.id.result_encryption_text);
+
+ vSigStatusIcon = (ImageView) itemView.findViewById(R.id.result_signature_icon);
+ vSigStatusText = (TextView) itemView.findViewById(R.id.result_signature_text);
+ vSignatureLayout = itemView.findViewById(R.id.result_signature_layout);
+ vSignatureName = (TextView) itemView.findViewById(R.id.result_signature_name);
+ vSignatureMail= (TextView) itemView.findViewById(R.id.result_signature_email);
+ vSignatureAction = (TextView) itemView.findViewById(R.id.result_signature_action);
+
+ vContextMenu = itemView.findViewById(R.id.context_menu);
+
+ }
+
+ @Override
+ public ImageView getEncryptionStatusIcon() {
+ return vEncStatusIcon;
+ }
+
+ @Override
+ public TextView getEncryptionStatusText() {
+ return vEncStatusText;
+ }
+
+ @Override
+ public ImageView getSignatureStatusIcon() {
+ return vSigStatusIcon;
+ }
+
+ @Override
+ public TextView getSignatureStatusText() {
+ return vSigStatusText;
+ }
+
+ @Override
+ public View getSignatureLayout() {
+ return vSignatureLayout;
+ }
+
+ @Override
+ public TextView getSignatureAction() {
+ return vSignatureAction;
+ }
+
+ @Override
+ public TextView getSignatureUserName() {
+ return vSignatureName;
+ }
+
+ @Override
+ public TextView getSignatureUserEmail() {
+ return vSignatureMail;
+ }
+
+ @Override
+ public boolean hasEncrypt() {
+ return true;
+ }
+ }
+
+ private Drawable loadIcon(String mimeType) {
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setType(mimeType);
+
+ final List<ResolveInfo> matches = getActivity()
+ .getPackageManager().queryIntentActivities(intent, 0);
+ //noinspection LoopStatementThatDoesntLoop
+ for (ResolveInfo match : matches) {
+ return match.loadIcon(getActivity().getPackageManager());
+ }
+ return null;
+
+ }
+
+}
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 3d87ce894..0626326fc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -43,12 +43,14 @@ import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
@@ -56,8 +58,9 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.Preferences;
-public abstract class DecryptFragment extends CryptoOperationFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+public abstract class DecryptFragment
+ extends CachingCryptoOperationFragment<PgpDecryptVerifyInputParcel, DecryptVerifyResult>
+ implements LoaderManager.LoaderCallbacks<Cursor> {
public static final int LOADER_ID_UNIFIED = 0;
public static final String ARG_DECRYPT_VERIFY_RESULT = "decrypt_verify_result";
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 b7ea90a36..1dcda5b8d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
@@ -17,11 +17,8 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -35,9 +32,6 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.ShareHelper;
@@ -115,7 +109,7 @@ public class DecryptTextFragment extends DecryptFragment {
mShowMenuOptions = args.getBoolean(ARG_SHOW_MENU, false);
if (savedInstanceState == null) {
- cryptoOperation(new CryptoInputParcel());
+ cryptoOperation();
}
}
@@ -158,77 +152,8 @@ public class DecryptTextFragment extends DecryptFragment {
}
@Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- intent.setAction(KeychainService.ACTION_DECRYPT_VERIFY);
-
- PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(mCiphertext.getBytes());
- data.putParcelable(KeychainService.DECRYPT_VERIFY_PARCEL, input);
- data.putParcelable(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
-
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_decrypting),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- DecryptVerifyResult pgpResult =
- returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
-
- if (pgpResult.success()) {
- byte[] decryptedMessage = pgpResult.getOutputBytes();
- String displayMessage;
- if (pgpResult.getCharset() != null) {
- try {
- displayMessage = new String(decryptedMessage, pgpResult.getCharset());
- } catch (UnsupportedEncodingException e) {
- // if we can't decode properly, just fall back to utf-8
- displayMessage = new String(decryptedMessage);
- }
- } else {
- displayMessage = new String(decryptedMessage);
- }
- mText.setText(displayMessage);
-
- // display signature result in activity
- loadVerifyResult(pgpResult);
- } else {
- // TODO: show also invalid layout with different text?
- }
- pgpResult.createNotify(getActivity()).show(DecryptTextFragment.this);
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
+ protected PgpDecryptVerifyInputParcel createOperationInput() {
+ return new PgpDecryptVerifyInputParcel(mCiphertext.getBytes());
}
@Override
@@ -236,4 +161,27 @@ public class DecryptTextFragment extends DecryptFragment {
mShowMenuOptions = hideErrorOverlay;
getActivity().supportInvalidateOptionsMenu();
}
+
+ @Override
+ protected void onCryptoOperationSuccess(DecryptVerifyResult result) {
+
+ byte[] decryptedMessage = result.getOutputBytes();
+ String displayMessage;
+ if (result.getCharset() != null) {
+ try {
+ displayMessage = new String(decryptedMessage, result.getCharset());
+ } catch (UnsupportedEncodingException e) {
+ // if we can't decode properly, just fall back to utf-8
+ displayMessage = new String(decryptedMessage);
+ }
+ } else {
+ displayMessage = new String(decryptedMessage);
+ }
+ mText.setText(displayMessage);
+
+ // display signature result in activity
+ loadVerifyResult(result);
+
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index 63fb8413b..48aa3016d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -26,6 +26,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
+import android.os.Parcelable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -39,6 +40,9 @@ import android.widget.ListView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
@@ -66,9 +70,8 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
-
-public class EditKeyFragment extends CryptoOperationFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+public class EditKeyFragment extends CryptoOperationFragment<SaveKeyringParcel, OperationResult>
+ implements LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri";
public static final String ARG_SAVE_KEYRING_PARCEL = "save_keyring_parcel";
@@ -572,7 +575,7 @@ public class EditKeyFragment extends CryptoOperationFragment implements
addSubkeyDialogFragment.show(getActivity().getSupportFragmentManager(), "addSubkeyDialog");
}
- private void returnKeyringParcel() {
+ protected void returnKeyringParcel() {
if (mSaveKeyringParcel.mAddUserIds.size() == 0) {
Notify.create(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR).show();
return;
@@ -591,76 +594,6 @@ public class EditKeyFragment extends CryptoOperationFragment implements
getActivity().finish();
}
- @Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
-
- Log.d(Constants.TAG, "cryptoInput:\n" + cryptoInput);
- Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel);
-
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_saving),
- ProgressDialog.STYLE_HORIZONTAL,
- true
- ) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
-
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final OperationResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- return;
- }
-
- // if bad -> display here!
- if (!result.success()) {
- result.createNotify(getActivity()).show();
- return;
- }
-
- // if good -> finish, return result to showkey and display there!
- Intent intent = new Intent();
- intent.putExtra(OperationResult.EXTRA_RESULT, result);
- getActivity().setResult(EditKeyActivity.RESULT_OK, intent);
- getActivity().finish();
-
- }
- }
- };
-
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
- intent.setAction(KeychainService.ACTION_EDIT_KEYRING);
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putParcelable(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
- data.putParcelable(KeychainService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
- }
-
/**
* Closes this activity, returning a result parcel with a single error log entry.
*/
@@ -675,4 +608,20 @@ public class EditKeyFragment extends CryptoOperationFragment implements
getActivity().finish();
}
+ @Override
+ protected SaveKeyringParcel createOperationInput() {
+ return mSaveKeyringParcel;
+ }
+
+ @Override
+ protected void onCryptoOperationSuccess(OperationResult result) {
+
+ // if good -> finish, return result to showkey and display there!
+ Intent intent = new Intent();
+ intent.putExtra(OperationResult.EXTRA_RESULT, result);
+ getActivity().setResult(EditKeyActivity.RESULT_OK, intent);
+ getActivity().finish();
+
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
index ddfdecca3..ba626cf11 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
@@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
+import org.sufficientlysecure.keychain.service.KeychainNewService;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
@@ -72,7 +73,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
-public class EncryptFilesFragment extends CachingCryptoOperationFragment<SignEncryptParcel> {
+public class EncryptFilesFragment
+ extends CachingCryptoOperationFragment<SignEncryptParcel, SignEncryptResult> {
public static final String ARG_DELETE_AFTER_ENCRYPT = "delete_after_encrypt";
public static final String ARG_ENCRYPT_FILENAMES = "encrypt_filenames";
@@ -272,11 +274,13 @@ public class EncryptFilesFragment extends CachingCryptoOperationFragment<SignEnc
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.encrypt_save: {
- cryptoOperation(false);
+ mShareAfterEncrypt = false;
+ cryptoOperation();
break;
}
case R.id.encrypt_share: {
- cryptoOperation(true);
+ mShareAfterEncrypt = true;
+ cryptoOperation();
break;
}
case R.id.check_use_armor: {
@@ -374,7 +378,9 @@ public class EncryptFilesFragment extends CachingCryptoOperationFragment<SignEnc
}
- public void onEncryptSuccess(final SignEncryptResult result) {
+ @Override
+ protected void onCryptoOperationSuccess(final SignEncryptResult result) {
+
if (mDeleteAfterEncrypt) {
DeleteFileDialogFragment deleteFileDialog =
DeleteFileDialogFragment.newInstance(mFilesAdapter.getAsArrayList());
@@ -402,6 +408,7 @@ public class EncryptFilesFragment extends CachingCryptoOperationFragment<SignEnc
result.createNotify(getActivity()).show();
}
}
+
}
// prepares mOutputUris, either directly and returns false, or indirectly
@@ -441,7 +448,46 @@ public class EncryptFilesFragment extends CachingCryptoOperationFragment<SignEnc
}
}
- protected SignEncryptParcel createIncompleteEncryptBundle() {
+ protected SignEncryptParcel createOperationInput() {
+
+ SignEncryptParcel actionsParcel = getCachedActionsParcel();
+
+ // we have three cases here: nothing cached, cached except output, fully cached
+ if (actionsParcel == null) {
+
+ // clear output uris for now, they will be created by prepareOutputStreams later
+ mOutputUris = null;
+
+ actionsParcel = createIncompleteCryptoInput();
+ // this is null if invalid, just return in that case
+ if (actionsParcel == null) {
+ return null;
+ }
+
+ cacheActionsParcel(actionsParcel);
+
+ }
+
+ // if it's incomplete, prepare output streams
+ if (actionsParcel.isIncomplete()) {
+ // if this is still null, prepare output streams again
+ if (mOutputUris == null) {
+ // this may interrupt the flow, and call us again from onActivityResult
+ if (prepareOutputStreams(mShareAfterEncrypt)) {
+ return null;
+ }
+ }
+
+ actionsParcel.addOutputUris(mOutputUris);
+ cacheActionsParcel(actionsParcel);
+
+ }
+
+ return actionsParcel;
+
+ }
+
+ protected SignEncryptParcel createIncompleteCryptoInput() {
// fill values for this action
SignEncryptParcel data = new SignEncryptParcel();
@@ -546,92 +592,6 @@ public class EncryptFilesFragment extends CachingCryptoOperationFragment<SignEnc
return sendIntent;
}
- public void cryptoOperation(boolean share) {
- mShareAfterEncrypt = share;
- cryptoOperation();
- }
-
- @Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput, SignEncryptParcel actionsParcel) {
-
- // we have three cases here: nothing cached, cached except output, fully cached
- if (actionsParcel == null) {
-
- // clear output uris for now, they will be created by prepareOutputStreams later
- mOutputUris = null;
-
- actionsParcel = createIncompleteEncryptBundle();
- // this is null if invalid, just return in that case
- if (actionsParcel == null) {
- // Notify was created by createEncryptBundle.
- return;
- }
-
- cacheActionsParcel(actionsParcel);
- }
-
- // if it's incomplete, prepare output streams
- if (actionsParcel.isIncomplete()) {
- // if this is still null, prepare output streams again
- if (mOutputUris == null) {
- // this may interrupt the flow, and call us again from onActivityResult
- if (prepareOutputStreams(mShareAfterEncrypt)) {
- return;
- }
- }
-
- actionsParcel.addOutputUris(mOutputUris);
- cacheActionsParcel(actionsParcel);
- }
-
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
- intent.setAction(KeychainService.ACTION_SIGN_ENCRYPT);
-
- Bundle data = new Bundle();
- data.putParcelable(KeychainService.SIGN_ENCRYPT_PARCEL, actionsParcel);
- data.putParcelable(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in KeychainService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_encrypting),
- ProgressDialog.STYLE_HORIZONTAL,
- true
- ) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- SignEncryptResult result =
- message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT);
- if (result.success()) {
- onEncryptSuccess(result);
- } else {
- result.createNotify(getActivity()).show();
- }
- }
- }
- };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- serviceHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
- }
-
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
@@ -646,7 +606,8 @@ public class EncryptFilesFragment extends CachingCryptoOperationFragment<SignEnc
if (resultCode == Activity.RESULT_OK && data != null) {
mOutputUris = new ArrayList<>(1);
mOutputUris.add(data.getData());
- cryptoOperation(false);
+ mShareAfterEncrypt = false;
+ cryptoOperation();
}
return;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java
index e206169bb..83fede917 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java
@@ -18,11 +18,8 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
@@ -41,9 +38,6 @@ import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
@@ -55,7 +49,8 @@ import org.sufficientlysecure.keychain.util.ShareHelper;
import java.util.HashSet;
import java.util.Set;
-public class EncryptTextFragment extends CachingCryptoOperationFragment<SignEncryptParcel> {
+public class EncryptTextFragment
+ extends CachingCryptoOperationFragment<SignEncryptParcel, SignEncryptResult> {
public static final String ARG_TEXT = "text";
public static final String ARG_USE_COMPRESSION = "use_compression";
@@ -145,6 +140,7 @@ public class EncryptTextFragment extends CachingCryptoOperationFragment<SignEncr
}
setHasOptionsMenu(true);
+
}
@Override
@@ -168,11 +164,13 @@ public class EncryptTextFragment extends CachingCryptoOperationFragment<SignEncr
// break;
// }
case R.id.encrypt_copy: {
- cryptoOperation(false);
+ mShareAfterEncrypt = false;
+ cryptoOperation();
break;
}
case R.id.encrypt_share: {
- cryptoOperation(true);
+ mShareAfterEncrypt = true;
+ cryptoOperation();
break;
}
default: {
@@ -204,21 +202,7 @@ public class EncryptTextFragment extends CachingCryptoOperationFragment<SignEncr
}
- protected void onEncryptSuccess(SignEncryptResult result) {
- if (mShareAfterEncrypt) {
- // Share encrypted message/file
- startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes()));
- } else {
- // Copy to clipboard
- copyToClipboard(result.getResultBytes());
- result.createNotify(getActivity()).show();
- // Notify.create(EncryptTextActivity.this,
- // R.string.encrypt_sign_clipboard_successful, Notify.Style.OK)
- // .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
- }
- }
-
- protected SignEncryptParcel createEncryptBundle() {
+ protected SignEncryptParcel createOperationInput() {
if (mMessage == null || mMessage.isEmpty()) {
Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR)
@@ -331,71 +315,21 @@ public class EncryptTextFragment extends CachingCryptoOperationFragment<SignEncr
return sendIntent;
}
- public void cryptoOperation(boolean share) {
- mShareAfterEncrypt = share;
- cryptoOperation();
- }
-
@Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput, SignEncryptParcel actionsParcel) {
+ protected void onCryptoOperationSuccess(SignEncryptResult result) {
- if (actionsParcel == null) {
-
- actionsParcel = createEncryptBundle();
- // this is null if invalid, just return in that case
- if (actionsParcel == null) {
- // Notify was created by inputIsValid.
- return;
- }
-
- cacheActionsParcel(actionsParcel);
+ if (mShareAfterEncrypt) {
+ // Share encrypted message/file
+ startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes()));
+ } else {
+ // Copy to clipboard
+ copyToClipboard(result.getResultBytes());
+ result.createNotify(getActivity()).show();
+ // Notify.create(EncryptTextActivity.this,
+ // R.string.encrypt_sign_clipboard_successful, Notify.Style.OK)
+ // .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
}
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
- intent.setAction(KeychainService.ACTION_SIGN_ENCRYPT);
-
- Bundle data = new Bundle();
- data.putParcelable(KeychainService.SIGN_ENCRYPT_PARCEL, actionsParcel);
- data.putParcelable(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in KeychainService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_encrypting),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- SignEncryptResult result =
- message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT);
-
- if (result.success()) {
- onEncryptSuccess(result);
- } else {
- result.createNotify(getActivity()).show();
- }
- }
- }
- };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- serviceHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
}
}
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 06d824f94..07ab88b02 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -388,12 +388,7 @@ public class ImportKeysActivity extends BaseNfcActivity {
return;
}
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- this,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- true
- ) {
+ ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -435,7 +430,11 @@ public class ImportKeysActivity extends BaseNfcActivity {
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- serviceHandler.showProgressDialog(this);
+ serviceHandler.showProgressDialog(
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL,
+ true
+ );
// start service with intent
startService(intent);
@@ -469,7 +468,10 @@ public class ImportKeysActivity extends BaseNfcActivity {
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- serviceHandler.showProgressDialog(this);
+ serviceHandler.showProgressDialog(
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL, true
+ );
// start service with intent
startService(intent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
index 42efbf0f8..9f3beff43 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
@@ -206,12 +206,7 @@ public class ImportKeysProxyActivity extends FragmentActivity {
private void startImportService(ArrayList<ParcelableKeyRing> keyRings) {
// Message is received after importing is done in KeychainService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- this,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- true
- ) {
+ ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -273,7 +268,9 @@ public class ImportKeysProxyActivity extends FragmentActivity {
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- serviceHandler.showProgressDialog(this);
+ serviceHandler.showProgressDialog(
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL, true);
// start service with intent
startService(intent);
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 ddc527847..36074f6ba 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -572,12 +572,7 @@ public class KeyListFragment extends LoaderFragment
keyList.add(keyEntry);
}
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_updating),
- ProgressDialog.STYLE_HORIZONTAL,
- true
- ) {
+ ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -625,7 +620,9 @@ public class KeyListFragment extends LoaderFragment
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- serviceHandler.showProgressDialog(getActivity());
+ serviceHandler.showProgressDialog(
+ getString(R.string.progress_updating),
+ ProgressDialog.STYLE_HORIZONTAL, true);
// start service with intent
getActivity().startService(intent);
@@ -633,11 +630,7 @@ public class KeyListFragment extends LoaderFragment
private void consolidate() {
// Message is received after importing is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -675,7 +668,9 @@ public class KeyListFragment extends LoaderFragment
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- saveHandler.showProgressDialog(getActivity());
+ saveHandler.showProgressDialog(
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL, false);
// start service with intent
getActivity().startService(intent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
index 273acc23d..7408135ae 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
@@ -124,12 +124,7 @@ public class SafeSlingerActivity extends BaseActivity {
final FragmentActivity activity = SafeSlingerActivity.this;
// Message is received after importing is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- activity,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- true
- ) {
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(activity) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -202,7 +197,10 @@ public class SafeSlingerActivity extends BaseActivity {
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- saveHandler.showProgressDialog(activity);
+ saveHandler.showProgressDialog(
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL, true
+ );
// start service with intent
activity.startService(intent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index 2a195a4da..e4dff6083 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -108,11 +108,7 @@ public class UploadKeyActivity extends BaseActivity {
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Message is received after uploading is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- this,
- getString(R.string.progress_uploading),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -132,7 +128,9 @@ public class UploadKeyActivity extends BaseActivity {
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- saveHandler.showProgressDialog(this);
+ saveHandler.showProgressDialog(
+ getString(R.string.progress_uploading),
+ ProgressDialog.STYLE_HORIZONTAL, false);
// start service with intent
startService(intent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 995b07720..5f6a32e5a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -698,9 +698,6 @@ public class ViewKeyActivity extends BaseNfcActivity implements
Messenger messenger = new Messenger(serviceHandler);
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
- // show progress dialog
- serviceHandler.showProgressDialog(this);
-
// start service with intent
startService(intent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
index 1f5c540ba..c33485adc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
@@ -360,12 +360,7 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
mProofVerifyDetail.setVisibility(View.GONE);
// Create a new Messenger for the communication back after proof work is done
- //
- ServiceProgressHandler handler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_verifying_signature),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
+ ServiceProgressHandler handler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -454,7 +449,10 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- handler.showProgressDialog(getActivity());
+ handler.showProgressDialog(
+ getString(R.string.progress_verifying_signature),
+ ProgressDialog.STYLE_HORIZONTAL, false
+ );
// start service with intent
getActivity().startService(intent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java
index d0b6f502f..8ed4cbc87 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java
@@ -1,26 +1,29 @@
package org.sufficientlysecure.keychain.ui.base;
+import android.app.ProgressDialog;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
+import android.os.Messenger;
import android.os.Parcelable;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.service.KeychainNewService;
+import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-public abstract class CachingCryptoOperationFragment <T extends Parcelable> extends CryptoOperationFragment {
+public abstract class CachingCryptoOperationFragment <T extends Parcelable, S extends OperationResult>
+ extends CryptoOperationFragment<T, S> {
public static final String ARG_CACHED_ACTIONS = "cached_actions";
private T mCachedActionsParcel;
@Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
- cryptoOperation(cryptoInput, mCachedActionsParcel);
- }
-
- @Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -37,21 +40,69 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable> exte
}
@Override
- public boolean handlePendingMessage(Message message) {
- // see if it's an InputPendingResult, and if so don't care
- if (super.handlePendingMessage(message)) {
- return true;
- }
+ protected void onCryptoOperationResult(S result) {
+ super.onCryptoOperationResult(result);
+ mCachedActionsParcel = null;
+ }
+
+ protected abstract T createOperationInput();
+
+ protected void cryptoOperation(CryptoInputParcel cryptoInput) {
+
+ if (mCachedActionsParcel == null) {
+
+ mCachedActionsParcel = createOperationInput();
+ // this is null if invalid, just return in that case
+ if (mCachedActionsParcel == null) {
+ // Notify was created by createCryptoInput.
+ return;
+ }
- // if it's a non-input-pending OKAY message, always clear the cached actions parcel
- if (message.arg1 == ServiceProgressHandler.MessageStatus.OKAY.ordinal()) {
- mCachedActionsParcel = null;
}
- return false;
+ // Send all information needed to service to edit key in other thread
+ Intent intent = new Intent(getActivity(), KeychainNewService.class);
+
+ intent.putExtra(KeychainNewService.EXTRA_OPERATION_INPUT, mCachedActionsParcel);
+ intent.putExtra(KeychainNewService.EXTRA_CRYPTO_INPUT, cryptoInput);
+
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
+ @Override
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == MessageStatus.OKAY.ordinal()) {
+
+ // get returned data bundle
+ Bundle returnData = message.getData();
+ if (returnData == null) {
+ return;
+ }
+
+ final OperationResult result =
+ returnData.getParcelable(OperationResult.EXTRA_RESULT);
+
+ onHandleResult(result);
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+
+ saveHandler.showProgressDialog(
+ getString(R.string.progress_building_key),
+ ProgressDialog.STYLE_HORIZONTAL, false);
+
+ getActivity().startService(intent);
+
}
- protected abstract void cryptoOperation(CryptoInputParcel cryptoInput, T cachedActionsParcel);
+ protected T getCachedActionsParcel() {
+ return mCachedActionsParcel;
+ }
protected void cacheActionsParcel(T cachedActionsParcel) {
mCachedActionsParcel = cachedActionsParcel;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java
index 7fc5eb1f4..0bba2f964 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java
@@ -19,24 +19,32 @@
package org.sufficientlysecure.keychain.ui.base;
import android.app.Activity;
+import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
import android.support.v4.app.Fragment;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.service.KeychainNewService;
+import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
+import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
/**
* All fragments executing crypto operations need to extend this class.
*/
-public abstract class CryptoOperationFragment extends Fragment {
+public abstract class CryptoOperationFragment <T extends Parcelable, S extends OperationResult>
+ extends Fragment {
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
public static final int REQUEST_CODE_NFC = 0x00008002;
@@ -99,35 +107,122 @@ public abstract class CryptoOperationFragment extends Fragment {
}
}
- public boolean handlePendingMessage(Message message) {
+ protected void dismissProgress() {
- if (message.arg1 == ServiceProgressHandler.MessageStatus.OKAY.ordinal()) {
- Bundle data = message.getData();
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) getFragmentManager().findFragmentByTag("progressDialog");
- OperationResult result = data.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null || !(result instanceof InputPendingResult)) {
- return false;
+ if (progressDialogFragment == null) {
+ return;
+ }
+
+ progressDialogFragment.dismissAllowingStateLoss();
+
+ }
+
+ protected abstract T createOperationInput();
+
+ protected void cryptoOperation(CryptoInputParcel cryptoInput) {
+
+ T operationInput = createOperationInput();
+ if (operationInput == null) {
+ return;
+ }
+
+ // Send all information needed to service to edit key in other thread
+ Intent intent = new Intent(getActivity(), KeychainNewService.class);
+
+ intent.putExtra(KeychainNewService.EXTRA_OPERATION_INPUT, operationInput);
+ intent.putExtra(KeychainNewService.EXTRA_CRYPTO_INPUT, cryptoInput);
+
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
+ @Override
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == MessageStatus.OKAY.ordinal()) {
+
+ // get returned data bundle
+ Bundle returnData = message.getData();
+ if (returnData == null) {
+ return;
+ }
+
+ final OperationResult result =
+ returnData.getParcelable(OperationResult.EXTRA_RESULT);
+
+ onHandleResult(result);
+ }
}
- InputPendingResult pendingResult = (InputPendingResult) result;
- if (pendingResult.isPending()) {
- RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
- initiateInputActivity(requiredInput);
- return true;
+ @Override
+ protected void onSetProgress(String msg, int progress, int max) {
+ // allow handling of progress in fragment, or delegate upwards
+ if ( ! onCryptoSetProgress(msg, progress, max)) {
+ super.onSetProgress(msg, progress, max);
+ }
}
- }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+
+ saveHandler.showProgressDialog(
+ getString(R.string.progress_building_key),
+ ProgressDialog.STYLE_HORIZONTAL, false);
+
+ getActivity().startService(intent);
- return false;
}
protected void cryptoOperation() {
cryptoOperation(new CryptoInputParcel());
}
- protected abstract void cryptoOperation(CryptoInputParcel cryptoInput);
+ protected void onCryptoOperationResult(S result) {
+ if (result.success()) {
+ onCryptoOperationSuccess(result);
+ } else {
+ onCryptoOperationError(result);
+ }
+ }
+
+ abstract protected void onCryptoOperationSuccess(S result);
+
+ protected void onCryptoOperationError(S result) {
+ result.createNotify(getActivity()).show(this);
+ }
protected void onCryptoOperationCancelled() {
- // Nothing to do here, in most cases
+ }
+
+ public void onHandleResult(OperationResult result) {
+
+ if (result instanceof InputPendingResult) {
+ InputPendingResult pendingResult = (InputPendingResult) result;
+ if (pendingResult.isPending()) {
+ RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
+ initiateInputActivity(requiredInput);
+ return;
+ }
+ }
+
+ dismissProgress();
+
+ try {
+ // noinspection unchecked, because type erasure :(
+ onCryptoOperationResult((S) result);
+ } catch (ClassCastException e) {
+ throw new AssertionError("bad return class ("
+ + result.getClass().getSimpleName() + "), this is a programming error!");
+ }
+
+ }
+
+ protected boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 58dce50a7..076876b5b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -135,12 +135,7 @@ public class DeleteKeyDialogFragment extends DialogFragment {
intent.setAction(KeychainService.ACTION_DELETE);
// Message is received after importing is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_deleting),
- ProgressDialog.STYLE_HORIZONTAL,
- true
- ) {
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
public void handleMessage(Message message) {
super.handleMessage(message);
@@ -168,7 +163,8 @@ public class DeleteKeyDialogFragment extends DialogFragment {
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- saveHandler.showProgressDialog(getActivity());
+ saveHandler.showProgressDialog(getString(R.string.progress_deleting),
+ ProgressDialog.STYLE_HORIZONTAL, true);
// start service with intent
getActivity().startService(intent);
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 dd85a6e46..11524aa08 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
@@ -24,9 +24,11 @@ import android.graphics.PorterDuff;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
+import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.nist.NISTNamedCurves;
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
@@ -34,6 +36,8 @@ import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.util.Log;
@@ -376,7 +380,6 @@ public class KeyFormattingUtils {
/**
* Converts the given bytes to a unique RGB color using SHA1 algorithm
*
- * @param bytes
* @return an integer array containing 3 numeric color representations (Red, Green, Black)
* @throws java.security.NoSuchAlgorithmException
* @throws java.security.DigestException
@@ -394,7 +397,7 @@ public class KeyFormattingUtils {
public static final int DEFAULT_COLOR = -1;
- public static enum State {
+ public enum State {
REVOKED,
EXPIRED,
VERIFIED,
@@ -420,9 +423,165 @@ public class KeyFormattingUtils {
setStatusImage(context, statusIcon, statusText, state, color, false);
}
+ public interface StatusHolder {
+ ImageView getEncryptionStatusIcon();
+ TextView getEncryptionStatusText();
+
+ ImageView getSignatureStatusIcon();
+ TextView getSignatureStatusText();
+
+ View getSignatureLayout();
+ TextView getSignatureUserName();
+ TextView getSignatureUserEmail();
+ TextView getSignatureAction();
+
+ boolean hasEncrypt();
+
+ }
+
+ @SuppressWarnings("deprecation") // context.getDrawable is api lvl 21, need to use deprecated
+ public static void setStatus(Context context, StatusHolder holder, DecryptVerifyResult result) {
+
+ OpenPgpSignatureResult signatureResult = result.getSignatureResult();
+
+ if (holder.hasEncrypt()) {
+ int encText, encIcon, encColor;
+ if (signatureResult != null && signatureResult.isSignatureOnly()) {
+ encIcon = R.drawable.status_lock_open_24dp;
+ encText = R.string.decrypt_result_not_encrypted;
+ encColor = R.color.android_red_light;
+ } else {
+ encIcon = R.drawable.status_lock_closed_24dp;
+ encText = R.string.decrypt_result_encrypted;
+ encColor = R.color.android_green_light;
+ }
+
+ int encColorRes = context.getResources().getColor(encColor);
+ holder.getEncryptionStatusIcon().setImageDrawable(context.getResources().getDrawable(encIcon));
+ holder.getEncryptionStatusIcon().setColorFilter(encColorRes, PorterDuff.Mode.SRC_IN);
+ holder.getEncryptionStatusText().setText(encText);
+ holder.getEncryptionStatusText().setTextColor(encColorRes);
+ }
+
+ int sigText, sigIcon, sigColor;
+ int sigActionText, sigActionIcon;
+
+ if (signatureResult == null) {
+
+ sigText = R.string.decrypt_result_no_signature;
+ sigIcon = R.drawable.status_signature_invalid_cutout_24dp;
+ sigColor = R.color.bg_gray;
+
+ // won't be used, but makes compiler happy
+ sigActionText = 0;
+ sigActionIcon = 0;
+
+ } else switch (signatureResult.getStatus()) {
+
+ case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {
+ sigText = R.string.decrypt_result_signature_certified;
+ sigIcon = R.drawable.status_signature_verified_cutout_24dp;
+ sigColor = R.color.android_green_light;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {
+ sigText = R.string.decrypt_result_signature_uncertified;
+ sigIcon = R.drawable.status_signature_unverified_cutout_24dp;
+ sigColor = R.color.android_orange_light;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ case OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED: {
+ sigText = R.string.decrypt_result_signature_revoked_key;
+ sigIcon = R.drawable.status_signature_revoked_cutout_24dp;
+ sigColor = R.color.android_red_light;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ case OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED: {
+ sigText = R.string.decrypt_result_signature_expired_key;
+ sigIcon = R.drawable.status_signature_expired_cutout_24dp;
+ sigColor = R.color.android_red_light;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ case OpenPgpSignatureResult.SIGNATURE_KEY_MISSING: {
+ sigText = R.string.decrypt_result_signature_missing_key;
+ sigIcon = R.drawable.status_signature_unknown_cutout_24dp;
+ sigColor = R.color.android_red_light;
+
+ sigActionText = R.string.decrypt_result_action_Lookup;
+ sigActionIcon = R.drawable.ic_file_download_grey_24dp;
+ break;
+ }
+
+ default:
+ case OpenPgpSignatureResult.SIGNATURE_ERROR: {
+ sigText = R.string.decrypt_result_invalid_signature;
+ sigIcon = R.drawable.status_signature_invalid_cutout_24dp;
+ sigColor = R.color.android_red_light;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ }
+
+ int sigColorRes = context.getResources().getColor(sigColor);
+ holder.getSignatureStatusIcon().setImageDrawable(context.getResources().getDrawable(sigIcon));
+ holder.getSignatureStatusIcon().setColorFilter(sigColorRes, PorterDuff.Mode.SRC_IN);
+ holder.getSignatureStatusText().setText(sigText);
+ holder.getSignatureStatusText().setTextColor(sigColorRes);
+
+ if (signatureResult != null) {
+
+ holder.getSignatureLayout().setVisibility(View.VISIBLE);
+
+ holder.getSignatureAction().setText(sigActionText);
+ holder.getSignatureAction().setCompoundDrawablesWithIntrinsicBounds(
+ 0, 0, sigActionIcon, 0);
+
+ String userId = signatureResult.getPrimaryUserId();
+ KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
+ if (userIdSplit.name != null) {
+ holder.getSignatureUserName().setText(userIdSplit.name);
+ } else {
+ holder.getSignatureUserName().setText(R.string.user_id_no_name);
+ }
+ if (userIdSplit.email != null) {
+ holder.getSignatureUserEmail().setVisibility(View.VISIBLE);
+ holder.getSignatureUserEmail().setText(userIdSplit.email);
+ } else {
+ holder.getSignatureUserEmail().setVisibility(View.GONE);
+ }
+
+ } else {
+
+ holder.getSignatureLayout().setVisibility(View.GONE);
+
+ }
+
+
+ }
+
/**
* Sets status image based on constant
*/
+ @SuppressWarnings("deprecation") // context.getDrawable is api lvl 21
public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText,
State state, int color, boolean big) {
switch (state) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
index 88b0289d4..2fd09dc79 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
@@ -97,10 +97,7 @@ public class ExportHelper {
intent.putExtra(KeychainService.EXTRA_DATA, data);
// Message is received after exporting is done in KeychainService
- ServiceProgressHandler exportHandler = new ServiceProgressHandler(mActivity,
- mActivity.getString(R.string.progress_exporting),
- ProgressDialog.STYLE_HORIZONTAL
- ) {
+ ServiceProgressHandler exportHandler = new ServiceProgressHandler(mActivity) {
@Override
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -121,7 +118,10 @@ public class ExportHelper {
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
// show progress dialog
- exportHandler.showProgressDialog(mActivity);
+ exportHandler.showProgressDialog(
+ mActivity.getString(R.string.progress_exporting),
+ ProgressDialog.STYLE_HORIZONTAL, false
+ );
// start service with intent
mActivity.startService(intent);
diff --git a/OpenKeychain/src/main/res/layout/decrypt_files_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_files_fragment.xml
deleted file mode 100644
index 22ee7e09f..000000000
--- a/OpenKeychain/src/main/res/layout/decrypt_files_fragment.xml
+++ /dev/null
@@ -1,149 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <ScrollView
- android:fillViewport="true"
- android:paddingTop="8dp"
- android:layout_width="match_parent"
- android:scrollbars="vertical"
- android:layout_height="0dp"
- android:layout_weight="1">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <View
- android:id="@+id/status_divider"
- android:layout_height="1dip"
- android:layout_width="match_parent"
- android:background="?android:attr/listDivider" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="4dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal"
-
- android:id="@+id/decrypt_files_browse"
- android:clickable="true"
- android:background="?android:selectableItemBackground">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingLeft="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/label_file_colon"
- android:gravity="center_vertical" />
-
- <TextView
- android:id="@+id/decrypt_files_filename"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:hint="@string/filemanager_title_open"
- android:drawableRight="@drawable/ic_folder_grey_24dp"
- android:drawablePadding="8dp"
- android:gravity="center_vertical" />
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"
- android:layout_marginBottom="8dp" />
-
- <CheckBox
- android:id="@+id/decrypt_files_delete_after_decryption"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_delete_after_decryption" />
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:id="@+id/decrypt_files_action_decrypt"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:text="@string/btn_decrypt_verify_file"
- android:clickable="true"
- android:background="?android:selectableItemBackground"
- android:drawableRight="@drawable/ic_save_grey_24dp"
- android:drawablePadding="8dp"
- android:gravity="center_vertical"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"
- android:layout_above="@+id/decrypt_files_action_decrypt" />
-
- </RelativeLayout>
- </LinearLayout>
- </LinearLayout>
- </ScrollView>
- </LinearLayout>
-
- <!-- TODO: Use this layout later to hide file list -->
- <LinearLayout
- android:visibility="gone"
- android:id="@+id/decrypt_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"></LinearLayout>
-
- <LinearLayout
- android:visibility="gone"
- android:id="@+id/decrypt_error_overlay"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center_vertical">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/decrypt_invalid_text"
- android:padding="16dp"
- android:layout_gravity="center"
- android:textColor="@color/android_red_light" />
-
- <Button
- android:id="@+id/decrypt_error_overlay_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/button_edgy"
- android:textColor="@color/android_red_light"
- android:text="@string/decrypt_invalid_button"
- android:layout_gravity="center_horizontal" />
- </LinearLayout>
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/decrypt_files_input_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_files_input_fragment.xml
new file mode 100644
index 000000000..b7e70ce10
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/decrypt_files_input_fragment.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="4dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <View
+ android:id="@+id/status_divider"
+ android:layout_height="1dip"
+ android:layout_width="match_parent"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:id="@+id/decrypt_files_browse"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/label_file_colon"
+ android:gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/decrypt_files_filename"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:hint="@string/filemanager_title_open"
+ android:drawableRight="@drawable/ic_folder_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="8dp" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/decrypt_files_action_decrypt"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:text="@string/btn_decrypt_verify_file"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground"
+ android:drawableRight="@drawable/ic_save_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_above="@+id/decrypt_files_action_decrypt" />
+
+ </RelativeLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/decrypt_files_list_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_files_list_fragment.xml
new file mode 100644
index 000000000..640b308bc
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/decrypt_files_list_fragment.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/decrypt_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/decrypted_files_list"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="16dp"
+ android:scrollbars="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:visibility="gone"
+ android:id="@+id/decrypt_error_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/decrypt_invalid_text"
+ android:padding="16dp"
+ android:layout_gravity="center"
+ android:textColor="@color/android_red_light" />
+
+ <Button
+ android:id="@+id/decrypt_error_overlay_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/button_edgy"
+ android:textColor="@color/android_red_light"
+ android:text="@string/decrypt_invalid_button"
+ android:layout_gravity="center_horizontal" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/decrypt_list_entry.xml b/OpenKeychain/src/main/res/layout/decrypt_list_entry.xml
new file mode 100644
index 000000000..5832c1ff7
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/decrypt_list_entry.xml
@@ -0,0 +1,243 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.CardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:card_view="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/card_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ card_view:cardBackgroundColor="@android:color/white"
+ card_view:cardElevation="2dp"
+ card_view:cardUseCompatPadding="true"
+ card_view:cardCornerRadius="4dp" >
+
+ <ViewAnimator
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:paddingRight="8dp"
+ android:paddingLeft="8dp"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out"
+ android:id="@+id/view_animator">
+
+ <!-- -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+
+ <ProgressBar
+ android:id="@+id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:indeterminate="false"
+ style="@style/Widget.AppCompat.ProgressBar.Horizontal"
+ android:progress="40"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/progress_msg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text=""
+ tools:text="Progress Message"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
+ <!-- -->
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:ignore="UseCompoundDrawables">
+
+ <ImageView
+ android:id="@+id/result_encryption_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_lock_open_24dp"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/result_encryption_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:text=""
+ tools:text="Encryption status text" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:paddingLeft="4dp"
+ tools:ignore="UseCompoundDrawables">
+
+ <ImageView
+ android:id="@+id/result_signature_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_signature_unverified_cutout_24dp"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/result_signature_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:text=""
+ tools:text="Signature status text" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/result_signature_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingRight="4dp"
+ android:paddingLeft="4dp"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/result_signature_name"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text=""
+ tools:text="Alice" />
+
+ <TextView
+ android:id="@+id/result_signature_email"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:text=""
+ tools:text="alice@example.com" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="1dip"
+ android:layout_height="match_parent"
+ android:gravity="right"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/result_signature_action"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:drawableRight="@drawable/ic_vpn_key_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:text=""
+ tools:text="Show"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/file"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground"
+ >
+
+ <ImageView
+ android:id="@+id/thumbnail"
+ android:layout_gravity="center_vertical"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="center"
+ android:padding="6dp"
+ android:src="@drawable/ic_doc_generic_am" />
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/filename"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="end"
+ android:text=""
+ tools:text="filename.jpg" />
+
+ <TextView
+ android:id="@+id/filesize"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textSize="12sp"
+ android:ellipsize="end"
+ android:text=""
+ tools:text="14kb" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/context_menu"
+ android:scaleType="center"
+ android:layout_width="36dip"
+ android:layout_height="48dip"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground"
+ android:src="@drawable/abc_ic_menu_moreoverflow_mtrl_alpha" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </ViewAnimator>
+
+</android.support.v7.widget.CardView> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/menu/decrypt_item_context_menu.xml b/OpenKeychain/src/main/res/menu/decrypt_item_context_menu.xml
new file mode 100644
index 000000000..e18bdf8e4
--- /dev/null
+++ b/OpenKeychain/src/main/res/menu/decrypt_item_context_menu.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/view_log"
+ android:title="@string/view_log"
+ android:icon="@drawable/ic_view_list_grey_24dp"
+ />
+
+ <item
+ android:id="@+id/decrypt_save"
+ android:title="@string/btn_save"
+ android:icon="@drawable/ic_action_encrypt_file_24dp"
+ />
+
+ <item
+ android:id="@+id/decrypt_delete"
+ android:title="@string/btn_delete_original"
+ android:icon="@drawable/ic_delete_grey_24dp"
+ />
+
+</menu> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 7323d19cd..7200d9545 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -341,6 +341,7 @@
<item quantity="other">"exporting keys…"</item>
</plurals>
+ <string name="progress_start">"preparing operation…"</string>
<string name="progress_extracting_signature_key">"extracting signature key…"</string>
<string name="progress_extracting_key">"extracting key…"</string>
<string name="progress_preparing_streams">"preparing streams…"</string>
@@ -1311,6 +1312,8 @@
<string name="error_nfc">"NFC Error: %s"</string>
<string name="error_pin_nodefault">Default PIN was rejected!</string>
<string name="error_bluetooth_file">Error creating temporary file. Bluetooth sharing will fail.</string>
+ <string name="btn_delete_original">Delete original file</string>
+
<string name="snack_encrypt_filenames_on">"Filenames <b>are</b> encrypted."</string>
<string name="snack_encrypt_filenames_off">"Filenames <b>are not</b> encrypted."</string>
<string name="snack_armor_on">"Output encoded as Text."</string>
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
index d4a6a105e..6984f126e 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
@@ -158,7 +158,7 @@ public class CertifyOperationTest {
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds()));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
Assert.assertTrue("certification must succeed", result.success());
@@ -186,7 +186,7 @@ public class CertifyOperationTest {
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(), null,
mStaticRing2.getPublicKey().getUnorderedUserAttributes()));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
Assert.assertTrue("certification must succeed", result.success());
@@ -209,7 +209,7 @@ public class CertifyOperationTest {
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds()));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
Assert.assertFalse("certification with itself must fail!", result.success());
Assert.assertTrue("error msg must be about self certification",
@@ -228,7 +228,7 @@ public class CertifyOperationTest {
uids.add("nonexistent");
actions.add(new CertifyAction(1234L, uids));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
Assert.assertFalse("certification of nonexistent key must fail", result.success());
Assert.assertTrue("must contain error msg about not found",
@@ -240,7 +240,7 @@ public class CertifyOperationTest {
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds()));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(mKeyPhrase1));
Assert.assertFalse("certification of nonexistent key must fail", result.success());
Assert.assertTrue("must contain error msg about not found",
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
index ce7414a3d..9c3636d07 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
@@ -171,6 +171,10 @@ public class PgpEncryptDecryptTest {
out.toByteArray(), plaintext.getBytes());
Assert.assertNull("signature should be an error", result.getSignatureResult());
+ CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel();
+ Assert.assertEquals("cached session keys must be empty",
+ 0, cryptoInput.getCryptoData().size());
+
OpenPgpMetadata metadata = result.getDecryptMetadata();
Assert.assertEquals("filesize must be correct",
out.toByteArray().length, metadata.getOriginalSize());
@@ -272,6 +276,10 @@ public class PgpEncryptDecryptTest {
out.toByteArray(), plaintext.getBytes());
Assert.assertNull("signature be empty", result.getSignatureResult());
+ CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel();
+ Assert.assertEquals("must have one cached session key",
+ 1, cryptoInput.getCryptoData().size());
+
OpenPgpMetadata metadata = result.getDecryptMetadata();
Assert.assertEquals("filesize must be correct",
out.toByteArray().length, metadata.getOriginalSize());
@@ -289,6 +297,10 @@ public class PgpEncryptDecryptTest {
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
+ CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel();
+ Assert.assertEquals("must have one cached session key",
+ 1, cryptoInput.getCryptoData().size());
+
Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
out.toByteArray(), plaintext.getBytes());