diff options
Diffstat (limited to 'OpenKeychain/src/main/java')
41 files changed, 1748 insertions, 1326 deletions
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); |