diff options
Diffstat (limited to 'OpenKeychain/src/main')
37 files changed, 1014 insertions, 637 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java index d64587578..f04d84315 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.helper; +import android.content.Context; import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -67,4 +68,12 @@ public class OtherHelper { return sb; } + public static int dpToPx(Context context, int dp) { + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5); + } + + public static int pxToDp(Context context, int px) { + return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 3681d62d8..9b070175c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -149,7 +149,8 @@ public class PgpImportExport { SaveKeyringResult result; if (key.isSecret()) { - result = mProviderHelper.saveSecretKeyRing(key); + result = mProviderHelper.saveSecretKeyRing(key, + new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100)); } else { result = mProviderHelper.savePublicKeyRing(key, new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index bbdbfffd2..c590200ee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -174,7 +174,7 @@ public class PgpKeyOperation { } catch(InvalidAlgorithmParameterException e) { throw new RuntimeException(e); } catch(PGPException e) { - throw new PgpGeneralMsgIdException(R.string.msg_mr_error_pgp, e); + throw new PgpGeneralMsgIdException(R.string.msg_mf_error_pgp, e); } } @@ -203,7 +203,7 @@ public class PgpKeyOperation { * 6. If requested, change passphrase */ - log.add(LogLevel.START, LogType.MSG_MR, indent); + log.add(LogLevel.START, LogType.MSG_MF, indent); indent += 1; updateProgress(R.string.progress_building_key, 0, 100); @@ -213,14 +213,14 @@ public class PgpKeyOperation { PGPSecretKey masterSecretKey = sKR.getSecretKey(); // 1. Unlock private key - log.add(LogLevel.DEBUG, LogType.MSG_MR_UNLOCK, indent); + log.add(LogLevel.DEBUG, LogType.MSG_MF_UNLOCK, indent); PGPPrivateKey masterPrivateKey; { try { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); } catch (PGPException e) { - log.add(LogLevel.ERROR, LogType.MSG_MR_UNLOCK_ERROR, indent+1); + log.add(LogLevel.ERROR, LogType.MSG_MF_UNLOCK_ERROR, indent+1); return null; } } @@ -237,7 +237,7 @@ public class PgpKeyOperation { // 2a. Add certificates for new user ids for (String userId : saveParcel.addUserIds) { - log.add(LogLevel.INFO, LogType.MSG_MR_UID_ADD, indent); + log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent); PGPSignature cert = generateUserIdSignature(masterPrivateKey, masterPublicKey, userId, false); modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); @@ -245,7 +245,7 @@ public class PgpKeyOperation { // 2b. Add revocations for revoked user ids for (String userId : saveParcel.revokeUserIds) { - log.add(LogLevel.INFO, LogType.MSG_MR_UID_REVOKE, indent); + log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent); PGPSignature cert = generateRevocationSignature(masterPrivateKey, masterPublicKey, userId); modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); @@ -253,7 +253,7 @@ public class PgpKeyOperation { // 3. If primary user id changed, generate new certificates for both old and new if (saveParcel.changePrimaryUserId != null) { - log.add(LogLevel.INFO, LogType.MSG_MR_UID_PRIMARY, indent); + log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent); // todo } @@ -267,18 +267,18 @@ public class PgpKeyOperation { // 4a. For each subkey change, generate new subkey binding certificate for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) { - log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_CHANGE, + log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE, new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent); PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId); if (sKey == null) { - log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_MISSING, + log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING, new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent + 1); return null; } PGPPublicKey pKey = sKey.getPublicKey(); if (change.mExpiry != null && new Date(change.mExpiry).before(new Date())) { - log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_PAST_EXPIRY, + log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent + 1); return null; } @@ -292,11 +292,11 @@ public class PgpKeyOperation { // 4b. For each subkey revocation, generate new subkey revocation certificate for (long revocation : saveParcel.revokeSubKeys) { - log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_REVOKE, + log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_REVOKE, new String[] { PgpKeyHelper.convertKeyIdToHex(revocation) }, indent); PGPSecretKey sKey = sKR.getSecretKey(revocation); if (sKey == null) { - log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_MISSING, + log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING, new String[] { PgpKeyHelper.convertKeyIdToHex(revocation) }, indent+1); return null; } @@ -314,13 +314,13 @@ public class PgpKeyOperation { try { if (add.mExpiry != null && new Date(add.mExpiry).before(new Date())) { - log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_PAST_EXPIRY, indent +1); + log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1); return null; } - log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_NEW, indent); + log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent); PGPSecretKey sKey = createKey(add.mAlgorithm, add.mKeysize, passphrase, false); - log.add(LogLevel.DEBUG, LogType.MSG_MR_SUBKEY_NEW_ID, + log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID, new String[] { PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()) }, indent+1); PGPPublicKey pKey = sKey.getPublicKey(); @@ -336,7 +336,7 @@ public class PgpKeyOperation { // 6. If requested, change passphrase if (saveParcel.newPassphrase != null) { - log.add(LogLevel.INFO, LogType.MSG_MR_PASSPHRASE, indent); + log.add(LogLevel.INFO, LogType.MSG_MF_PASSPHRASE, indent); PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build() .get(HashAlgorithmTags.SHA1); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( @@ -352,17 +352,17 @@ public class PgpKeyOperation { // This one must only be thrown by } catch (IOException e) { - log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_ENCODE, indent+1); + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1); return null; } catch (PGPException e) { - log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_PGP, indent+1); + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1); return null; } catch (SignatureException e) { - log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_SIG, indent+1); + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SIG, indent+1); return null; } - log.add(LogLevel.OK, LogType.MSG_MR_SUCCESS, indent); + log.add(LogLevel.OK, LogType.MSG_MF_SUCCESS, indent); return new UncachedKeyRing(sKR); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index f22ea7697..e1ce62bdf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -27,10 +27,14 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; +import java.util.TreeSet; import java.util.Vector; /** Wrapper around PGPKeyRing class, to be constructed from bytes. @@ -50,24 +54,27 @@ import java.util.Vector; * @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey * */ +@SuppressWarnings("unchecked") public class UncachedKeyRing { final PGPKeyRing mRing; final boolean mIsSecret; + final boolean mIsCanonicalized; UncachedKeyRing(PGPKeyRing ring) { mRing = ring; mIsSecret = ring instanceof PGPSecretKeyRing; + mIsCanonicalized = false; } - public long getMasterKeyId() { - return mRing.getPublicKey().getKeyID(); + private UncachedKeyRing(PGPKeyRing ring, boolean canonicalized) { + mRing = ring; + mIsSecret = ring instanceof PGPSecretKeyRing; + mIsCanonicalized = canonicalized; } - /* TODO don't use this */ - @Deprecated - public PGPKeyRing getRing() { - return mRing; + public long getMasterKeyId() { + return mRing.getPublicKey().getKeyID(); } public UncachedPublicKey getPublicKey() { @@ -94,6 +101,10 @@ public class UncachedKeyRing { return mIsSecret; } + public boolean isCanonicalized() { + return mIsCanonicalized; + } + public byte[] getEncoded() throws IOException { return mRing.getEncoded(); } @@ -102,15 +113,6 @@ public class UncachedKeyRing { return mRing.getPublicKey().getFingerprint(); } - public static UncachedKeyRing decodePublicFromData(byte[] data) - throws PgpGeneralException, IOException { - UncachedKeyRing ring = decodeFromData(data); - if(ring.isSecret()) { - throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!"); - } - return ring; - } - public static UncachedKeyRing decodeFromData(byte[] data) throws PgpGeneralException, IOException { BufferedInputStream bufferedInput = @@ -178,7 +180,7 @@ public class UncachedKeyRing { return result; } - /** "Canonicalizes" a key, removing inconsistencies in the process. This operation can be + /** "Canonicalizes" a public key, removing inconsistencies in the process. This variant can be * applied to public keyrings only. * * More specifically: @@ -193,19 +195,18 @@ public class UncachedKeyRing { * - certifications and certification revocations for user ids * - If a subkey retains no valid subkey binding certificate, remove it * - If a user id retains no valid self certificate, remove it + * - If the key is a secret key, remove all certificates by foreign keys + * - If no valid user id remains, log an error and return null * * This operation writes an OperationLog which can be used as part of a OperationResultParcel. * - * @return A canonicalized key + * @return A canonicalized key, or null on fatal error * */ + @SuppressWarnings("ConstantConditions") public UncachedKeyRing canonicalize(OperationLog log, int indent) { - if (isSecret()) { - throw new RuntimeException("Tried to canonicalize non-secret keyring. " + - "This is a programming error and should never happen!"); - } - log.add(LogLevel.START, LogType.MSG_KC, + log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC, new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); indent += 1; @@ -213,7 +214,7 @@ public class UncachedKeyRing { int redundantCerts = 0, badCerts = 0; - PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; + PGPKeyRing ring = mRing; PGPPublicKey masterKey = mRing.getPublicKey(); final long masterKeyId = masterKey.getKeyID(); @@ -334,8 +335,15 @@ public class UncachedKeyRing { continue; } - // If this is a foreign signature, never mind any further + // If this is a foreign signature, ... if (certId != masterKeyId) { + // never mind any further for public keys, but remove them from secret ones + if (isSecret()) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_FOREIGN, + new String[] { PgpKeyHelper.convertKeyIdToHex(certId) }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + badCerts += 1; + } continue; } @@ -433,7 +441,7 @@ public class UncachedKeyRing { } // Replace modified key in the keyring - ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + ring = replacePublicKey(ring, modified); indent -= 1; } @@ -578,7 +586,7 @@ public class UncachedKeyRing { // it is not properly bound? error! if (selfCert == null) { - ring = PGPPublicKeyRing.removePublicKey(ring, modified); + ring = replacePublicKey(ring, modified); log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT, new String[]{ PgpKeyHelper.convertKeyIdToHex(key.getKeyID()) }, indent); @@ -593,7 +601,7 @@ public class UncachedKeyRing { modified = PGPPublicKey.addCertification(modified, revocation); } // replace pubkey in keyring - ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + ring = replacePublicKey(ring, modified); indent -= 1; } @@ -611,8 +619,181 @@ public class UncachedKeyRing { log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, indent); } - return new UncachedKeyRing(ring); + return new UncachedKeyRing(ring, true); + } + + /** This operation merges information from a different keyring, returning a combined + * UncachedKeyRing. + * + * The combined keyring contains the subkeys and user ids of both input keyrings, but it does + * not necessarily have the canonicalized property. + * + * @param other The UncachedKeyRing to merge. Must not be empty, and of the same masterKeyId + * @return A consolidated UncachedKeyRing with the data of both input keyrings. Same type as + * this object, or null on error. + * + */ + public UncachedKeyRing merge(UncachedKeyRing other, OperationLog log, int indent) { + + log.add(LogLevel.DEBUG, isSecret() ? LogType.MSG_MG_SECRET : LogType.MSG_MG_PUBLIC, + new String[]{ PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()) }, indent); + indent += 1; + + long masterKeyId = other.getMasterKeyId(); + + if (getMasterKeyId() != masterKeyId) { + log.add(LogLevel.ERROR, LogType.MSG_MG_HETEROGENEOUS, null, indent); + return null; + } + + // remember which certs we already added. this is cheaper than semantic deduplication + Set<byte[]> certs = new TreeSet<byte[]>(new Comparator<byte[]>() { + public int compare(byte[] left, byte[] right) { + // check for length equality + if (left.length != right.length) { + return left.length - right.length; + } + // compare byte-by-byte + for (int i = 0; i < left.length && i < right.length; i++) { + if (left[i] != right[i]) { + return (left[i] & 0xff) - (right[i] & 0xff); + } + } + // ok they're the same + return 0; + }}); + + try { + PGPKeyRing result = mRing; + PGPKeyRing candidate = other.mRing; + + // Pre-load all existing certificates + for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(result.getPublicKeys())) { + for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getSignatures())) { + certs.add(cert.getEncoded()); + } + } + + // keep track of the number of new certs we add + int newCerts = 0; + + for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(candidate.getPublicKeys())) { + + final PGPPublicKey resultKey = result.getPublicKey(key.getKeyID()); + if (resultKey == null) { + log.add(LogLevel.DEBUG, LogType.MSG_MG_NEW_SUBKEY, null, indent); + result = replacePublicKey(result, key); + continue; + } + + // Modifiable version of the old key, which we merge stuff into (keep old for comparison) + PGPPublicKey modified = resultKey; + + // Iterate certifications + for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getSignatures())) { + int type = cert.getSignatureType(); + // Disregard certifications on user ids, we will deal with those later + if (type == PGPSignature.NO_CERTIFICATION + || type == PGPSignature.DEFAULT_CERTIFICATION + || type == PGPSignature.CASUAL_CERTIFICATION + || type == PGPSignature.POSITIVE_CERTIFICATION + || type == PGPSignature.CERTIFICATION_REVOCATION) { + continue; + } + + // Don't merge foreign stuff into secret keys + if (cert.getKeyID() != masterKeyId && isSecret()) { + continue; + } + + byte[] encoded = cert.getEncoded(); + // Known cert, skip it + if (certs.contains(encoded)) { + continue; + } + certs.add(encoded); + modified = PGPPublicKey.addCertification(modified, cert); + newCerts += 1; + } + + // If this is a subkey, merge it in and stop here + if (!key.isMasterKey()) { + if (modified != resultKey) { + result = replacePublicKey(result, modified); + } + continue; + } + + // Copy over all user id certificates + for (String userId : new IterableIterator<String>(key.getUserIDs())) { + for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getSignaturesForID(userId))) { + // Don't merge foreign stuff into secret keys + if (cert.getKeyID() != masterKeyId && isSecret()) { + continue; + } + byte[] encoded = cert.getEncoded(); + // Known cert, skip it + if (certs.contains(encoded)) { + continue; + } + newCerts += 1; + certs.add(encoded); + modified = PGPPublicKey.addCertification(modified, userId, cert); + } + } + // If anything changed, save the updated (sub)key + if (modified != resultKey) { + result = replacePublicKey(result, modified); + } + + } + + log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW, + new String[] { Integer.toString(newCerts) }, indent); + + return new UncachedKeyRing(result); + + } catch (IOException e) { + log.add(LogLevel.ERROR, LogType.MSG_MG_FATAL_ENCODE, null, indent); + return null; + } + + } + + public UncachedKeyRing extractPublicKeyRing() { + if(!isSecret()) { + throw new RuntimeException("Tried to extract public keyring from non-secret keyring. " + + "This is a programming error and should never happen!"); + } + + ArrayList<PGPPublicKey> keys = new ArrayList(); + Iterator<PGPPublicKey> it = mRing.getPublicKeys(); + while (it.hasNext()) { + keys.add(it.next()); + } + + return new UncachedKeyRing(new PGPPublicKeyRing(keys)); } + /** This method replaces a public key in a keyring. + * + * This method essentially wraps PGP*KeyRing.insertPublicKey, where the keyring may be of either + * the secret or public subclass. + * + * @return the resulting PGPKeyRing of the same type as the input + */ + private static PGPKeyRing replacePublicKey(PGPKeyRing ring, PGPPublicKey key) { + if (ring instanceof PGPPublicKeyRing) { + return PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) ring, key); + } + PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring; + PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID()); + // TODO generate secret key with S2K dummy, if none exists! for now, just die. + if (sKey == null) { + throw new RuntimeException("dummy secret key generation not yet implemented"); + } + sKey = PGPSecretKey.replacePublicKey(sKey, key); + return PGPSecretKeyRing.insertSecretKey(secRing, sKey); + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java index 71d237c05..6f3068261 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java @@ -91,8 +91,18 @@ public abstract class WrappedKeyRing extends KeyRing { getRing().encode(stream); } + /** Returns an UncachedKeyRing which wraps the same data as this ring. This method should + * only be used */ + public UncachedKeyRing getUncachedKeyRing() { + return new UncachedKeyRing(getRing()); + } + abstract PGPKeyRing getRing(); abstract public IterableIterator<WrappedPublicKey> publicKeyIterator(); + public UncachedKeyRing getUncached() { + return new UncachedKeyRing(getRing()); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java index 9591cf8bc..d7148f710 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java @@ -154,8 +154,4 @@ public class WrappedSecretKeyRing extends WrappedKeyRing { }); } - public UncachedKeyRing getUncached() { - return new UncachedKeyRing(mRing); - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 8688ecb6c..955fb90ba 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -57,6 +57,7 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -68,7 +69,7 @@ import java.util.Set; * name, it is not only a helper but actually the main interface for all * synchronous database operations. * - * Operations in this class write logs (TODO). These can be obtained from the + * Operations in this class write logs. These can be obtained from the * OperationResultParcel return values directly, but are also accumulated over * the lifetime of the executing ProviderHelper object unless the resetLog() * method is called to start a new one specifically. @@ -187,9 +188,9 @@ public class ProviderHelper { KeyRings.PUBKEY_DATA }, KeyRings.HAS_ANY_SECRET + " = 1", null, null); - LongSparseArray<WrappedPublicKey> result = - new LongSparseArray<WrappedPublicKey>(cursor.getCount()); try { + LongSparseArray<WrappedPublicKey> result = new LongSparseArray<WrappedPublicKey>(); + if (cursor != null && cursor.moveToFirst()) do { long masterKeyId = cursor.getLong(0); boolean hasAnySecret = cursor.getInt(1) > 0; @@ -200,13 +201,15 @@ public class ProviderHelper { new WrappedPublicKeyRing(blob, hasAnySecret, verified).getSubkey()); } } while (cursor.moveToNext()); + + return result; + } finally { if (cursor != null) { cursor.close(); } } - return result; } public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) { @@ -260,60 +263,30 @@ public class ProviderHelper { } } - public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { - return savePublicKeyRing(keyRing, new Progressable() { - @Override - public void setProgress(String message, int current, int total) { - return; - } - - @Override - public void setProgress(int resourceId, int current, int total) { - return; - } - - @Override - public void setProgress(int current, int total) { - return; - } - }); - } - /** - * Saves PGPPublicKeyRing with its keys and userIds in DB + /** Saves an UncachedKeyRing of the public variant into the db. + * + * This method will not delete all previous data for this masterKeyId from the database prior + * to inserting. All public data is effectively re-inserted, secret keyrings are left deleted + * and need to be saved externally to be preserved past the operation. */ @SuppressWarnings("unchecked") - public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing, Progressable progress) { + private int internalSavePublicKeyRing(UncachedKeyRing keyRing, + Progressable progress, boolean selfCertsAreTrusted) { if (keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; + } + if (!keyRing.isCanonicalized()) { + log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); + return SaveKeyringResult.RESULT_ERROR; } // start with ok result int result = SaveKeyringResult.SAVED_PUBLIC; long masterKeyId = keyRing.getMasterKeyId(); - log(LogLevel.START, LogType.MSG_IP, - new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); - mIndent += 1; - - // Canonicalize this key, to assert a number of assumptions made about it. - keyRing = keyRing.canonicalize(mLog, mIndent); - if (keyRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); - } - UncachedPublicKey masterKey = keyRing.getPublicKey(); - // IF there is a secret key, preserve it! - UncachedKeyRing secretRing; - try { - secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); - log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); - progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 30, 100); - } catch (NotFoundException e) { - secretRing = null; - } - ArrayList<ContentProviderOperation> operations; try { @@ -331,7 +304,7 @@ public class ProviderHelper { values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); @@ -463,6 +436,7 @@ public class ProviderHelper { item.isRevoked = true; log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); } + continue; } @@ -500,7 +474,7 @@ public class ProviderHelper { } mIndent -= 1; - progress.setProgress(LogType.MSG_IP_UID_REORDER.getMsgId(), 80, 100); + progress.setProgress(LogType.MSG_IP_UID_REORDER.getMsgId(), 65, 100); log(LogLevel.DEBUG, LogType.MSG_IP_UID_REORDER); // primary before regular before revoked (see UserIdItem.compareTo) // this is a stable sort, so the order of keys is otherwise preserved. @@ -511,7 +485,7 @@ public class ProviderHelper { operations.add(buildUserIdOperations(masterKeyId, item, userIdRank)); if (item.selfCert != null) { operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfCert, - secretRing != null ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); + selfCertsAreTrusted ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); } // don't bother with trusted certs if the uid is revoked, anyways if (item.isRevoked) { @@ -529,7 +503,7 @@ public class ProviderHelper { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); Log.e(Constants.TAG, "IOException during import", e); mIndent -= 1; - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } try { @@ -544,33 +518,24 @@ public class ProviderHelper { } log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); - progress.setProgress(LogType.MSG_IP_APPLY_BATCH.getMsgId(), 90, 100); + progress.setProgress(LogType.MSG_IP_APPLY_BATCH.getMsgId(), 75, 100); mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); - // Save the saved keyring (if any) - if (secretRing != null) { - log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); - mIndent += 1; - saveSecretKeyRing(secretRing); - result |= SaveKeyringResult.SAVED_SECRET; - mIndent -= 1; - } - - mIndent -= 1; log(LogLevel.OK, LogType.MSG_IP_SUCCESS); - progress.setProgress(LogType.MSG_IP_SUCCESS.getMsgId(), 100, 100); - return new SaveKeyringResult(result, mLog); + mIndent -= 1; + progress.setProgress(LogType.MSG_IP_SUCCESS.getMsgId(), 90, 100); + return result; } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); mIndent -= 1; - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } catch (OperationApplicationException e) { - log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EX); + log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EXC); Log.e(Constants.TAG, "OperationApplicationException during import", e); mIndent -= 1; - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; } } @@ -596,17 +561,19 @@ public class ProviderHelper { } } - /** - * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring - * is already in the database! - * - * TODO allow adding secret keys where no public key exists (ie, consolidate keys) + /** Saves an UncachedKeyRing of the secret variant into the db. + * This method will fail if no corresponding public keyring is in the database! */ - public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing keyRing) { + private int internalSaveSecretKeyRing(UncachedKeyRing keyRing) { if (!keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return SaveKeyringResult.RESULT_ERROR; + } + + if (!keyRing.isCanonicalized()) { + log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); + return SaveKeyringResult.RESULT_ERROR; } long masterKeyId = keyRing.getMasterKeyId(); @@ -614,6 +581,12 @@ public class ProviderHelper { new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); mIndent += 1; + // Canonicalize this key, to assert a number of assumptions made about it. + keyRing = keyRing.canonicalize(mLog, mIndent); + if (keyRing == null) { + return SaveKeyringResult.RESULT_ERROR; + } + // IF this is successful, it's a secret key int result = SaveKeyringResult.SAVED_SECRET; @@ -624,11 +597,14 @@ public class ProviderHelper { values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); // insert new version of this keyRing Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); - mContentResolver.insert(uri, values); + if (mContentResolver.insert(uri, values) == null) { + log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); + return SaveKeyringResult.RESULT_ERROR; + } } catch (IOException e) { Log.e(Constants.TAG, "Failed to encode key!", e); - log(LogLevel.ERROR, LogType.MSG_IS_IO_EXCPTION); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC); + return SaveKeyringResult.RESULT_ERROR; } { @@ -672,22 +648,219 @@ public class ProviderHelper { } log(LogLevel.OK, LogType.MSG_IS_SUCCESS); - return new SaveKeyringResult(result, mLog); + return result; + + } + + + @Deprecated + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { + return savePublicKeyRing(keyRing, new Progressable() { + @Override + public void setProgress(String message, int current, int total) { + } + + @Override + public void setProgress(int resourceId, int current, int total) { + } + + @Override + public void setProgress(int current, int total) { + } + }); + } + + /** Save a public keyring into the database. + * + * This is a high level method, which takes care of merging all new information into the old and + * keep public and secret keyrings in sync. + */ + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, Progressable progress) { + + try { + long masterKeyId = publicRing.getMasterKeyId(); + log(LogLevel.START, LogType.MSG_IP, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + mIndent += 1; + + // If there is an old keyring, merge it + try { + UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached(); + + // Merge data from new public ring into the old one + publicRing = oldPublicRing.merge(publicRing, mLog, mIndent); + + // If this is null, there is an error in the log so we can just return + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Canonicalize this keyring, to assert a number of assumptions made about it. + publicRing = publicRing.canonicalize(mLog, mIndent); + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Early breakout if nothing changed + if (Arrays.hashCode(publicRing.getEncoded()) + == Arrays.hashCode(oldPublicRing.getEncoded())) { + log(LogLevel.OK, LogType.MSG_IP_SUCCESS_IDENTICAL, null); + return new SaveKeyringResult(SaveKeyringResult.RESULT_OK, mLog); + } + } catch (NotFoundException e) { + // Not an issue, just means we are dealing with a new keyring. + + // Canonicalize this keyring, to assert a number of assumptions made about it. + publicRing = publicRing.canonicalize(mLog, mIndent); + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + } + + // If there is a secret key, merge new data (if any) and save the key for later + UncachedKeyRing secretRing; + try { + secretRing = getWrappedSecretKeyRing(publicRing.getMasterKeyId()).getUncached(); + + // Merge data from new public ring into secret one + secretRing = secretRing.merge(publicRing, mLog, mIndent); + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + secretRing = secretRing.canonicalize(mLog, mIndent); + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + } catch (NotFoundException e) { + // No secret key available (this is what happens most of the time) + secretRing = null; + } + + int result = internalSavePublicKeyRing(publicRing, progress, secretRing != null); + + // Save the saved keyring (if any) + if (secretRing != null) { + progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); + int secretResult = internalSaveSecretKeyRing(secretRing); + if ((secretResult & SaveKeyringResult.RESULT_ERROR) != SaveKeyringResult.RESULT_ERROR) { + result |= SaveKeyringResult.SAVED_SECRET; + } + } + + mIndent -= 1; + return new SaveKeyringResult(result, mLog); + + } catch (IOException e) { + log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + } + + public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing secretRing, Progressable progress) { + + try { + long masterKeyId = secretRing.getMasterKeyId(); + log(LogLevel.START, LogType.MSG_IS, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + mIndent += 1; + + // If there is an old secret key, merge it. + try { + UncachedKeyRing oldSecretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); + + // Merge data from new secret ring into old one + secretRing = oldSecretRing.merge(secretRing, mLog, mIndent); + + // If this is null, there is an error in the log so we can just return + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Canonicalize this keyring, to assert a number of assumptions made about it. + secretRing = secretRing.canonicalize(mLog, mIndent); + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // Early breakout if nothing changed + if (Arrays.hashCode(secretRing.getEncoded()) + == Arrays.hashCode(oldSecretRing.getEncoded())) { + log(LogLevel.OK, LogType.MSG_IS_SUCCESS_IDENTICAL, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + return new SaveKeyringResult(SaveKeyringResult.RESULT_OK, mLog); + } + } catch (NotFoundException e) { + // Not an issue, just means we are dealing with a new keyring + + // Canonicalize this keyring, to assert a number of assumptions made about it. + secretRing = secretRing.canonicalize(mLog, mIndent); + if (secretRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + } + + // Merge new data into public keyring as well, if there is any + UncachedKeyRing publicRing; + try { + UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached(); + + // Merge data from new public ring into secret one + publicRing = oldPublicRing.merge(secretRing, mLog, mIndent); + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + // If nothing changed, never mind + if (Arrays.hashCode(publicRing.getEncoded()) + == Arrays.hashCode(oldPublicRing.getEncoded())) { + publicRing = null; + } + + } catch (NotFoundException e) { + log(LogLevel.DEBUG, LogType.MSG_IS_PUBRING_GENERATE, null); + publicRing = secretRing.extractPublicKeyRing(); + } + + if (publicRing != null) { + publicRing = publicRing.canonicalize(mLog, mIndent); + if (publicRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + int result = internalSavePublicKeyRing(publicRing, progress, true); + if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + } + + progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); + int result = internalSaveSecretKeyRing(secretRing); + return new SaveKeyringResult(result, mLog); + + } catch (IOException e) { + log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC, null); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } } /** * Saves (or updates) a pair of public and secret KeyRings in the database */ - public void saveKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException { - long masterKeyId = pubRing.getPublicKey().getKeyId(); + @Deprecated // scheduled for deletion after merge with new-edit branch + public void savePairedKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException { + long masterKeyId = pubRing.getMasterKeyId(); // delete secret keyring (so it isn't unnecessarily saved by public-savePublicKeyRing below) mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null); // save public keyring - savePublicKeyRing(pubRing); - saveSecretKeyRing(secRing); + internalSavePublicKeyRing(pubRing, null, true); + internalSaveSecretKeyRing(secRing); } /** @@ -803,9 +976,6 @@ public class ProviderHelper { /** * Must be an uri pointing to an account - * - * @param uri - * @return */ public AppSettings getApiAppSettings(Uri uri) { AppSettings settings = null; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 73f552c92..6e49baf92 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -3,8 +3,10 @@ package org.sufficientlysecure.keychain.service; import android.os.Parcel; import android.os.Parcelable; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.IterableIterator; +import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -129,12 +131,11 @@ public class OperationResultParcel implements Parcelable { MSG_IP_DELETE_OLD_OK (R.string.msg_ip_delete_old_ok), MSG_IP_ENCODE_FAIL (R.string.msg_ip_encode_fail), MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), - MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), + MSG_IP_FAIL_OP_EXC (R.string.msg_ip_fail_op_exc), MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_keys), MSG_IP_PREPARE (R.string.msg_ip_prepare), - MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), MSG_IP_MASTER (R.string.msg_ip_master), MSG_IP_MASTER_EXPIRED (R.string.msg_ip_master_expired), @@ -159,6 +160,7 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_FLAGS_XXS (R.string.msg_ip_subkey_flags_xxs), MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), MSG_IP_SUCCESS (R.string.msg_ip_success), + MSG_IP_SUCCESS_IDENTICAL (R.string.msg_ip_success_identical), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), @@ -171,15 +173,19 @@ public class OperationResultParcel implements Parcelable { // import secret MSG_IS(R.string.msg_is), MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), + MSG_IS_DB_EXCEPTION (R.string.msg_is_db_exception), + MSG_IS_FAIL_IO_EXC (R.string.msg_is_io_exc), MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), - MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), + MSG_IS_PUBRING_GENERATE (R.string.msg_is_pubring_generate), MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), + MSG_IS_SUCCESS_IDENTICAL (R.string.msg_is_success_identical), MSG_IS_SUCCESS (R.string.msg_is_success), // keyring canonicalization - MSG_KC (R.string.msg_kc), + MSG_KC_PUBLIC (R.string.msg_kc_public), + MSG_KC_SECRET (R.string.msg_kc_secret), MSG_KC_FATAL_NO_UID (R.string.msg_kc_fatal_no_uid), MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), @@ -212,27 +218,38 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type), MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), + MSG_KC_UID_FOREIGN (R.string.msg_kc_uid_foreign), MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), - MSG_MR (R.string.msg_mr), - MSG_MR_ERROR_ENCODE (R.string.msg_mr_error_encode), - MSG_MR_ERROR_PGP (R.string.msg_mr_error_pgp), - MSG_MR_ERROR_SIG (R.string.msg_mr_error_sig), - MSG_MR_PASSPHRASE (R.string.msg_mr_passphrase), - MSG_MR_SUBKEY_CHANGE (R.string.msg_mr_subkey_change), - MSG_MR_SUBKEY_MISSING (R.string.msg_mr_subkey_missing), - MSG_MR_SUBKEY_NEW_ID (R.string.msg_mr_subkey_new_id), - MSG_MR_SUBKEY_NEW (R.string.msg_mr_subkey_new), - MSG_MR_SUBKEY_PAST_EXPIRY (R.string.msg_mr_subkey_past_expiry), - MSG_MR_SUBKEY_REVOKE (R.string.msg_mr_subkey_revoke), - MSG_MR_SUCCESS (R.string.msg_mr_success), - MSG_MR_UID_ADD (R.string.msg_mr_uid_add), - MSG_MR_UID_PRIMARY (R.string.msg_mr_uid_primary), - MSG_MR_UID_REVOKE (R.string.msg_mr_uid_revoke), - MSG_MR_UNLOCK_ERROR (R.string.msg_mr_unlock_error), - MSG_MR_UNLOCK (R.string.msg_mr_unlock), + + // keyring consolidation + MSG_MG_PUBLIC (R.string.msg_mg_public), + MSG_MG_SECRET (R.string.msg_mg_secret), + MSG_MG_FATAL_ENCODE (R.string.msg_mg_fatal_encode), + MSG_MG_HETEROGENEOUS (R.string.msg_mg_heterogeneous), + MSG_MG_NEW_SUBKEY (R.string.msg_mg_new_subkey), + MSG_MG_FOUND_NEW (R.string.msg_mg_found_new), + + // secret key modify + MSG_MF (R.string.msg_mr), + MSG_MF_ERROR_ENCODE (R.string.msg_mf_error_encode), + MSG_MF_ERROR_PGP (R.string.msg_mf_error_pgp), + MSG_MF_ERROR_SIG (R.string.msg_mf_error_sig), + MSG_MF_PASSPHRASE (R.string.msg_mf_passphrase), + MSG_MF_SUBKEY_CHANGE (R.string.msg_mf_subkey_change), + MSG_MF_SUBKEY_MISSING (R.string.msg_mf_subkey_missing), + MSG_MF_SUBKEY_NEW_ID (R.string.msg_mf_subkey_new_id), + MSG_MF_SUBKEY_NEW (R.string.msg_mf_subkey_new), + MSG_MF_SUBKEY_PAST_EXPIRY (R.string.msg_mf_subkey_past_expiry), + MSG_MF_SUBKEY_REVOKE (R.string.msg_mf_subkey_revoke), + MSG_MF_SUCCESS (R.string.msg_mf_success), + MSG_MF_UID_ADD (R.string.msg_mf_uid_add), + MSG_MF_UID_PRIMARY (R.string.msg_mf_uid_primary), + MSG_MF_UID_REVOKE (R.string.msg_mf_uid_revoke), + MSG_MF_UNLOCK_ERROR (R.string.msg_mf_unlock_error), + MSG_MF_UNLOCK (R.string.msg_mf_unlock), ; private final int mMsgId; @@ -279,6 +296,7 @@ public class OperationResultParcel implements Parcelable { /// Simple convenience method public void add(LogLevel level, LogType type, String[] parameters, int indent) { + Log.d(Constants.TAG, type.toString()); add(new OperationResultParcel.LogEntryParcel(level, type, parameters, indent)); } 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 f389726ff..54186e380 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -32,10 +32,14 @@ import android.os.Messenger; import android.os.Parcelable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; +import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.View; import android.view.View.OnClickListener; +import android.view.ViewGroup; import android.widget.ArrayAdapter; import com.github.johnpersano.supertoasts.SuperCardToast; @@ -45,18 +49,22 @@ import com.github.johnpersano.supertoasts.util.Style; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; +import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; +import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; +import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; import java.util.Locale; -public class ImportKeysActivity extends ActionBarActivity implements ActionBar.OnNavigationListener { +public class ImportKeysActivity extends ActionBarActivity { public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY"; public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_QR_CODE"; @@ -90,23 +98,18 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O private String[] mNavigationStrings; private Fragment mCurrentFragment; private View mImportButton; + private ViewPager mViewPager; + private SlidingTabLayout mSlidingTabLayout; + private PagerTabStripAdapter mTabsAdapter; + + public static final int VIEW_PAGER_HEIGHT = 64; // dp - private static final Class[] NAVIGATION_CLASSES = new Class[]{ - ImportKeysServerFragment.class, - ImportKeysFileFragment.class, - ImportKeysQrCodeFragment.class, - ImportKeysClipboardFragment.class, - ImportKeysNFCFragment.class, - ImportKeysKeybaseFragment.class - }; private static final int NAV_SERVER = 0; - private static final int NAV_FILE = 1; - private static final int NAV_QR_CODE = 2; - private static final int NAV_CLIPBOARD = 3; - private static final int NAV_NFC = 4; - private static final int NAV_KEYBASE = 5; + private static final int NAV_QR_CODE = 1; + private static final int NAV_FILE = 2; + private static final int NAV_KEYBASE = 3; - private int mCurrentNavPosition = -1; + private int mSwitchToTab = NAV_SERVER; @Override protected void onCreate(Bundle savedInstanceState) { @@ -114,6 +117,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O setContentView(R.layout.import_keys_activity); + mViewPager = (ViewPager) findViewById(R.id.import_pager); + mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.import_sliding_tab_layout); + mImportButton = findViewById(R.id.import_import); mImportButton.setOnClickListener(new OnClickListener() { @Override @@ -127,18 +133,55 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { setTitle(R.string.nav_import); } else { - getSupportActionBar().setDisplayShowTitleEnabled(false); - - // set drop down navigation - Context context = getSupportActionBar().getThemedContext(); - ArrayAdapter<CharSequence> navigationAdapter = ArrayAdapter.createFromResource(context, - R.array.import_action_list, android.R.layout.simple_spinner_dropdown_item); - getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - getSupportActionBar().setListNavigationCallbacks(navigationAdapter, this); + initTabs(); } handleActions(savedInstanceState, getIntent()); + } + + private void initTabs() { + mTabsAdapter = new PagerTabStripAdapter(this); + mViewPager.setAdapter(mTabsAdapter); + mSlidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + // resize view pager back to 64 if keyserver settings have been collapsed + if (getViewPagerHeight() > VIEW_PAGER_HEIGHT) { + resizeViewPager(VIEW_PAGER_HEIGHT); + } + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + }); + + Bundle serverBundle = new Bundle(); +// serverBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysServerFragment.class, + serverBundle, getString(R.string.import_tab_keyserver)); + + Bundle qrCodeBundle = new Bundle(); +// importBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysQrCodeFragment.class, + qrCodeBundle, getString(R.string.import_tab_qr_code)); + + Bundle fileBundle = new Bundle(); +// importBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysFileFragment.class, + fileBundle, getString(R.string.import_tab_direct)); + Bundle keybaseBundle = new Bundle(); +// keybaseBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysKeybaseFragment.class, + keybaseBundle, getString(R.string.import_tab_keybase)); + + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); } protected void handleActions(Bundle savedInstanceState, Intent intent) { @@ -164,7 +207,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O /* Keychain's own Actions */ // display file fragment - loadNavFragment(NAV_FILE, null); + mViewPager.setCurrentItem(NAV_FILE); if (dataUri != null) { // action: directly load data @@ -199,7 +242,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O // display keyserver fragment with query Bundle args = new Bundle(); args.putString(ImportKeysServerFragment.ARG_QUERY, query); - loadNavFragment(NAV_SERVER, args); +// loadNavFragment(NAV_SERVER, args); + //TODO: load afterwards! + mSwitchToTab = NAV_SERVER; // action: search immediately startListFragment(savedInstanceState, null, null, query); @@ -223,9 +268,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O return; } } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) { - // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_FILE, null); + mSwitchToTab = NAV_FILE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); @@ -233,26 +277,28 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O // also exposed in AndroidManifest // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_QR_CODE, null); + mSwitchToTab = NAV_QR_CODE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_NFC, null); + mSwitchToTab = NAV_QR_CODE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else if (ACTION_IMPORT_KEY_FROM_KEYBASE.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_KEYBASE, null); + mSwitchToTab = NAV_KEYBASE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else { startListFragment(savedInstanceState, null, null, null); } + + mViewPager.setCurrentItem(mSwitchToTab); } private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) { @@ -275,54 +321,16 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O getSupportFragmentManager().executePendingTransactions(); } - /** - * "Basically, when using a list navigation, onNavigationItemSelected() is automatically - * called when your activity is created/re-created, whether you like it or not. To prevent - * your Fragment's onCreateView() from being called twice, this initial automatic call to - * onNavigationItemSelected() should check whether the Fragment is already in existence - * inside your Activity." - * <p/> - * from http://stackoverflow.com/a/14295474 - * <p/> - * In our case, if we start ImportKeysActivity with parameters to directly search using a fingerprint, - * the fragment would be loaded twice resulting in the query being empty after the second load. - * <p/> - * Our solution: - * To prevent that a fragment will be loaded again even if it was already loaded loadNavFragment - * checks against mCurrentNavPosition. - * - * @param itemPosition - * @param itemId - * @return - */ - @Override - public boolean onNavigationItemSelected(int itemPosition, long itemId) { - Log.d(Constants.TAG, "onNavigationItemSelected"); - - loadNavFragment(itemPosition, null); - - return true; - } - - private void loadNavFragment(int itemPosition, Bundle args) { - if (mCurrentNavPosition != itemPosition) { - if (ActionBar.NAVIGATION_MODE_LIST == getSupportActionBar().getNavigationMode()) { - getSupportActionBar().setSelectedNavigationItem(itemPosition); - } - loadFragment(NAVIGATION_CLASSES[itemPosition], args, mNavigationStrings[itemPosition]); - mCurrentNavPosition = itemPosition; - } + public void resizeViewPager(int dp) { + ViewGroup.LayoutParams params = mViewPager.getLayoutParams(); + params.height = OtherHelper.dpToPx(this, dp); + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); } - private void loadFragment(Class<?> clss, Bundle args, String tag) { - mCurrentFragment = Fragment.instantiate(this, clss.getName(), args); - - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - // Replace whatever is in the fragment container with this fragment - // and give the fragment a tag name equal to the string at the position selected - ft.replace(R.id.import_navigation_fragment, mCurrentFragment, tag); - // Apply changes - ft.commit(); + public int getViewPagerHeight() { + ViewGroup.LayoutParams params = mViewPager.getLayoutParams(); + return OtherHelper.pxToDp(this, params.height); } public void loadFromFingerprintUri(Bundle savedInstanceState, Uri dataUri) { @@ -349,7 +357,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O Bundle args = new Bundle(); args.putString(ImportKeysServerFragment.ARG_QUERY, query); args.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); - loadNavFragment(NAV_SERVER, args); +// loadNavFragment(NAV_SERVER, args); + + //TODO // action: search directly startListFragment(savedInstanceState, null, null, query); @@ -437,15 +447,16 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setButtonTextColor(getResources().getColor(R.color.black)); toast.setTextColor(getResources().getColor(R.color.black)); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", - new SuperToast.OnClickListener() { - @Override - public void onClick(View view, Parcelable token) { - Intent intent = new Intent( - ImportKeysActivity.this, LogDisplayActivity.class); - intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); - startActivity(intent); + new SuperToast.OnClickListener() { + @Override + public void onClick(View view, Parcelable token) { + Intent intent = new Intent( + ImportKeysActivity.this, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + startActivity(intent); + } } - })); + )); toast.show(); /* @@ -560,9 +571,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O super.onResume(); // Check to see if the Activity started due to an Android Beam - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN - && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { - handleActionNdefDiscovered(getIntent()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { + handleActionNdefDiscovered(getIntent()); + } else { + Log.d(Constants.TAG, "NFC: No NDEF discovered!"); + } } else { Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java deleted file mode 100644 index f331358fa..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2013-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.net.Uri; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; - -import com.beardedhen.androidbootstrap.BootstrapButton; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; - -import java.util.Locale; - -public class ImportKeysClipboardFragment extends Fragment { - - private ImportKeysActivity mImportActivity; - private BootstrapButton mButton; - - /** - * Creates new instance of this fragment - */ - public static ImportKeysClipboardFragment newInstance() { - ImportKeysClipboardFragment frag = new ImportKeysClipboardFragment(); - - Bundle args = new Bundle(); - 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.import_keys_clipboard_fragment, container, false); - - mButton = (BootstrapButton) view.findViewById(R.id.import_clipboard_button); - mButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity()); - String sendText = ""; - if (clipboardText != null) { - sendText = clipboardText.toString(); - if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { - mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText)); - return; - } - } - mImportActivity.loadCallback(sendText.getBytes(), null, null, null, null); - } - }); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mImportActivity = (ImportKeysActivity) getActivity(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java index 51f961aab..060e9bab2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -19,21 +19,24 @@ package org.sufficientlysecure.keychain.ui; 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.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.FileHelper; +import java.util.Locale; + public class ImportKeysFileFragment extends Fragment { private ImportKeysActivity mImportActivity; - private BootstrapButton mBrowse; + private View mBrowse; + private View mClipboardButton; public static final int REQUEST_CODE_FILE = 0x00007003; @@ -56,26 +59,45 @@ public class ImportKeysFileFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.import_keys_file_fragment, container, false); - mBrowse = (BootstrapButton) view.findViewById(R.id.import_keys_file_browse); + mBrowse = view.findViewById(R.id.import_keys_file_browse); mBrowse.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { // open .asc or .gpg files - // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc + // setting it to text/plain prevents Cyanogenmod's file manager from selecting asc // or gpg types! FileHelper.openFile(ImportKeysFileFragment.this, Constants.Path.APP_DIR + "/", "*/*", REQUEST_CODE_FILE); } }); + mClipboardButton = view.findViewById(R.id.import_clipboard_button); + mClipboardButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity()); + String sendText = ""; + if (clipboardText != null) { + sendText = clipboardText.toString(); + if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { + mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText)); + return; + } + } + mImportActivity.loadCallback(sendText.getBytes(), null, null, null, null); + } + }); + + return view; } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onAttach(Activity activity) { + super.onAttach(activity); - mImportActivity = (ImportKeysActivity) getActivity(); + mImportActivity = (ImportKeysActivity) activity; } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java index a639fe0e0..9264829ea 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -29,8 +30,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.R; /** @@ -40,7 +39,7 @@ import org.sufficientlysecure.keychain.R; public class ImportKeysKeybaseFragment extends Fragment { private ImportKeysActivity mImportActivity; - private BootstrapButton mSearchButton; + private View mSearchButton; private EditText mQueryEditText; public static final String ARG_QUERY = "query"; @@ -66,7 +65,7 @@ public class ImportKeysKeybaseFragment extends Fragment { mQueryEditText = (EditText) view.findViewById(R.id.import_keybase_query); - mSearchButton = (BootstrapButton) view.findViewById(R.id.import_keybase_search); + mSearchButton = view.findViewById(R.id.import_keybase_search); mSearchButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -101,8 +100,6 @@ public class ImportKeysKeybaseFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mImportActivity = (ImportKeysActivity) getActivity(); - // set displayed values if (getArguments() != null) { if (getArguments().containsKey(ARG_QUERY)) { @@ -112,6 +109,13 @@ public class ImportKeysKeybaseFragment extends Fragment { } } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + mImportActivity = (ImportKeysActivity) activity; + } + private void search(String query) { mImportActivity.loadCallback(null, null, null, null, query); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index d91c55da3..7d8dc4a6c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -42,6 +42,7 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; @@ -97,7 +98,7 @@ public class ImportKeysListFragment extends ListFragment implements public ArrayList<ParcelableKeyRing> getSelectedData() { ArrayList<ParcelableKeyRing> result = new ArrayList<ParcelableKeyRing>(); - for(ImportKeysListEntry entry : getSelectedEntries()) { + for (ImportKeysListEntry entry : getSelectedEntries()) { result.add(mCachedKeyData.get(entry.getKeyId())); } return result; @@ -273,17 +274,15 @@ public class ImportKeysListFragment extends ListFragment implements // No error mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); } else if (error instanceof ImportKeysListLoader.FileHasNoContent) { - AppMsg.makeText(getActivity(), R.string.error_import_file_no_content, - AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.error_import_file_no_content, Notify.Style.ERROR); } else if (error instanceof ImportKeysListLoader.NonPgpPart) { - AppMsg.makeText(getActivity(), + Notify.showNotify(getActivity(), ((ImportKeysListLoader.NonPgpPart) error).getCount() + " " + getResources(). getQuantityString(R.plurals.error_import_non_pgp_part, ((ImportKeysListLoader.NonPgpPart) error).getCount()), - new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.confirm)).show(); + Notify.Style.OK); } else { - AppMsg.makeText(getActivity(), R.string.error_generic_report_bug, - new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.alert)).show(); + Notify.showNotify(getActivity(), R.string.error_generic_report_bug, Notify.Style.ERROR); } break; @@ -292,23 +291,17 @@ public class ImportKeysListFragment extends ListFragment implements // TODO: possibly fine-tune message building for these two cases if (error == null) { - AppMsg.makeText( - getActivity(), getResources().getQuantityString(R.plurals.keys_found, - mAdapter.getCount(), mAdapter.getCount()), - AppMsg.STYLE_INFO - ).show(); + // No error } else if (error instanceof Keyserver.QueryTooShortException) { - AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query, - AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.error_keyserver_insufficient_query, Notify.Style.ERROR); } else if (error instanceof Keyserver.TooManyResponsesException) { - AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses, - AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.error_keyserver_too_many_responses, Notify.Style.ERROR); } else if (error instanceof Keyserver.QueryFailedException) { Log.d(Constants.TAG, "Unrecoverable keyserver query error: " + error.getLocalizedMessage()); String alert = getActivity().getString(R.string.error_searching_keys); alert = alert + " (" + error.getLocalizedMessage() + ")"; - AppMsg.makeText(getActivity(), alert, AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), alert, Notify.Style.ERROR); } break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java deleted file mode 100644 index 45f464b1c..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2013-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.content.Intent; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; - -import com.beardedhen.androidbootstrap.BootstrapButton; - -import org.sufficientlysecure.keychain.R; - -public class ImportKeysNFCFragment extends Fragment { - - private BootstrapButton mButton; - - /** - * Creates new instance of this fragment - */ - public static ImportKeysNFCFragment newInstance() { - ImportKeysNFCFragment frag = new ImportKeysNFCFragment(); - - Bundle args = new Bundle(); - 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.import_keys_nfc_fragment, container, false); - - mButton = (BootstrapButton) view.findViewById(R.id.import_nfc_button); - mButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - // show nfc help - Intent intent = new Intent(getActivity(), HelpActivity.class); - intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, HelpActivity.TAB_NFC); - startActivityForResult(intent, 0); - } - }); - - return view; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java index 22b56e1ab..0cbb51c77 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java @@ -17,47 +17,47 @@ package org.sufficientlysecure.keychain.ui; +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.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; -import com.devspark.appmsg.AppMsg; import com.google.zxing.integration.android.IntentResult; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify; import java.util.ArrayList; import java.util.Locale; public class ImportKeysQrCodeFragment extends Fragment { - private ImportKeysActivity mImportActivity; - private BootstrapButton mButton; - private TextView mText; - private ProgressBar mProgress; + private View mNfcButton; + + private View mQrCodeButton; + private TextView mQrCodeText; + private ProgressBar mQrCodeProgress; - private String[] mScannedContent; + private String[] mQrCodeContent; /** * Creates new instance of this fragment */ - public static ImportKeysQrCodeFragment newInstance() { - ImportKeysQrCodeFragment frag = new ImportKeysQrCodeFragment(); + public static ImportKeysFileFragment newInstance() { + ImportKeysFileFragment frag = new ImportKeysFileFragment(); Bundle args = new Bundle(); - frag.setArguments(args); + frag.setArguments(args); return frag; } @@ -68,11 +68,23 @@ public class ImportKeysQrCodeFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.import_keys_qr_code_fragment, container, false); - mButton = (BootstrapButton) view.findViewById(R.id.import_qrcode_button); - mText = (TextView) view.findViewById(R.id.import_qrcode_text); - mProgress = (ProgressBar) view.findViewById(R.id.import_qrcode_progress); + mNfcButton = view.findViewById(R.id.import_nfc_button); + mNfcButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + // show nfc help + Intent intent = new Intent(getActivity(), HelpActivity.class); + intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, HelpActivity.TAB_NFC); + startActivityForResult(intent, 0); + } + }); + + mQrCodeButton = view.findViewById(R.id.import_qrcode_button); + mQrCodeText = (TextView) view.findViewById(R.id.import_qrcode_text); + mQrCodeProgress = (ProgressBar) view.findViewById(R.id.import_qrcode_progress); - mButton.setOnClickListener(new OnClickListener() { + mQrCodeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -85,10 +97,10 @@ public class ImportKeysQrCodeFragment extends Fragment { } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onAttach(Activity activity) { + super.onAttach(activity); - mImportActivity = (ImportKeysActivity) getActivity(); + mImportActivity = (ImportKeysActivity) activity; } @Override @@ -122,8 +134,7 @@ public class ImportKeysQrCodeFragment extends Fragment { } // fail... - AppMsg.makeText(getActivity(), R.string.import_qr_code_wrong, AppMsg.STYLE_ALERT) - .show(); + Notify.showNotify(getActivity(), R.string.import_qr_code_wrong, Notify.Style.ERROR); } break; @@ -136,6 +147,7 @@ public class ImportKeysQrCodeFragment extends Fragment { } } + public void importFingerprint(Uri dataUri) { mImportActivity.loadFromFingerprintUri(null, dataUri); } @@ -151,32 +163,31 @@ public class ImportKeysQrCodeFragment extends Fragment { // first qr code -> setup if (counter == 0) { - mScannedContent = new String[size]; - mProgress.setMax(size); - mProgress.setVisibility(View.VISIBLE); - mText.setVisibility(View.VISIBLE); + mQrCodeContent = new String[size]; + mQrCodeProgress.setMax(size); + mQrCodeProgress.setVisibility(View.VISIBLE); + mQrCodeText.setVisibility(View.VISIBLE); } - if (mScannedContent == null || counter > mScannedContent.length) { - AppMsg.makeText(getActivity(), R.string.import_qr_code_start_with_one, AppMsg.STYLE_ALERT) - .show(); + if (mQrCodeContent == null || counter > mQrCodeContent.length) { + Notify.showNotify(getActivity(), R.string.import_qr_code_start_with_one, Notify.Style.ERROR); return; } // save scanned content - mScannedContent[counter] = content; + mQrCodeContent[counter] = content; // get missing numbers ArrayList<Integer> missing = new ArrayList<Integer>(); - for (int i = 0; i < mScannedContent.length; i++) { - if (mScannedContent[i] == null) { + for (int i = 0; i < mQrCodeContent.length; i++) { + if (mQrCodeContent[i] == null) { missing.add(i); } } // update progress and text - int alreadyScanned = mScannedContent.length - missing.size(); - mProgress.setProgress(alreadyScanned); + int alreadyScanned = mQrCodeContent.length - missing.size(); + mQrCodeProgress.setProgress(alreadyScanned); String missingString = ""; for (int m : missing) { @@ -188,17 +199,16 @@ public class ImportKeysQrCodeFragment extends Fragment { String missingText = getResources().getQuantityString(R.plurals.import_qr_code_missing, missing.size(), missingString); - mText.setText(missingText); + mQrCodeText.setText(missingText); // finished! if (missing.size() == 0) { - mText.setText(R.string.import_qr_code_finished); + mQrCodeText.setText(R.string.import_qr_code_finished); String result = ""; - for (String in : mScannedContent) { + for (String in : mQrCodeContent) { result += in; } mImportActivity.loadCallback(result.getBytes(), null, null, null, null); } } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java index 9e3d88ff5..eabc8348c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -32,8 +33,6 @@ import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.Preferences; @@ -46,8 +45,10 @@ public class ImportKeysServerFragment extends Fragment { private ImportKeysActivity mImportActivity; - private BootstrapButton mSearchButton; + private View mSearchButton; private EditText mQueryEditText; + private View mConfigButton; + private View mConfigLayout; private Spinner mServerSpinner; private ArrayAdapter<String> mServerAdapter; @@ -73,14 +74,17 @@ public class ImportKeysServerFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.import_keys_server_fragment, container, false); - mSearchButton = (BootstrapButton) view.findViewById(R.id.import_server_search); + mSearchButton = view.findViewById(R.id.import_server_search); mQueryEditText = (EditText) view.findViewById(R.id.import_server_query); + mConfigButton = view.findViewById(R.id.import_server_config_button); + mConfigLayout = view.findViewById(R.id.import_server_config); mServerSpinner = (Spinner) view.findViewById(R.id.import_server_spinner); // add keyservers to spinner mServerAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_spinner_item, Preferences.getPreferences(getActivity()) - .getKeyServers()); + .getKeyServers() + ); mServerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mServerSpinner.setAdapter(mServerAdapter); if (mServerAdapter.getCount() > 0) { @@ -118,6 +122,17 @@ public class ImportKeysServerFragment extends Fragment { } }); + mConfigButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mImportActivity.getViewPagerHeight() > ImportKeysActivity.VIEW_PAGER_HEIGHT) { + mImportActivity.resizeViewPager(ImportKeysActivity.VIEW_PAGER_HEIGHT); + } else { + mImportActivity.resizeViewPager(ImportKeysActivity.VIEW_PAGER_HEIGHT + 41); + } + } + }); + return view; } @@ -125,8 +140,6 @@ public class ImportKeysServerFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mImportActivity = (ImportKeysActivity) getActivity(); - // set displayed values if (getArguments() != null) { if (getArguments().containsKey(ARG_QUERY)) { @@ -150,6 +163,13 @@ public class ImportKeysServerFragment extends Fragment { } } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + mImportActivity = (ImportKeysActivity) activity; + } + private void search(String query, String keyServer) { mImportActivity.loadCallback(null, null, query, keyServer, null); } 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 13e9d22ab..1912b6e7d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -171,7 +171,7 @@ public class ViewKeyActivity extends ActionBarActivity implements Bundle shareBundle = new Bundle(); shareBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); mTabsAdapter.addTab(ViewKeyShareFragment.class, - mainBundle, getString(R.string.key_view_tab_share)); + shareBundle, getString(R.string.key_view_tab_share)); // update layout after operations mSlidingTabLayout.setViewPager(mViewPager); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index 03a82696d..c2712e89e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -133,7 +133,7 @@ public class ImportKeysListLoader // read all available blocks... (asc files can contain many blocks with BEGIN END) while (bufferedInput.available() > 0) { - // todo deal with non-keyring objects? + // TODO: deal with non-keyring objects? List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput); for(UncachedKeyRing key : rings) { ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key); diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_action_collection.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_collection.png Binary files differnew file mode 100644 index 000000000..8de91173c --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_collection.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_action_qr_code.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_qr_code.png Binary files differnew file mode 100644 index 000000000..e15a055db --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_qr_code.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_action_collection.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_collection.png Binary files differnew file mode 100644 index 000000000..b89ea93ff --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_collection.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_action_qr_code.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_qr_code.png Binary files differnew file mode 100644 index 000000000..1c65e5af8 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_qr_code.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_collection.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_collection.png Binary files differnew file mode 100644 index 000000000..88240fd30 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_collection.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_qr_code.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_qr_code.png Binary files differnew file mode 100644 index 000000000..c56b128e4 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_qr_code.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_collection.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_collection.png Binary files differnew file mode 100644 index 000000000..c41ca8c8b --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_collection.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_qr_code.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_qr_code.png Binary files differnew file mode 100644 index 000000000..c718aee0b --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_qr_code.png diff --git a/OpenKeychain/src/main/res/layout/import_keys_activity.xml b/OpenKeychain/src/main/res/layout/import_keys_activity.xml index 0486b6bd6..fc9d21e23 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_activity.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_activity.xml @@ -3,7 +3,7 @@ android:id="@+id/content_frame" android:layout_marginLeft="@dimen/drawer_content_padding" android:layout_width="match_parent" - android:layout_height="fill_parent" + android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout @@ -12,21 +12,38 @@ android:layout_height="wrap_content" android:orientation="vertical" /> - <FrameLayout - android:id="@+id/import_navigation_fragment" + <org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout + android:id="@+id/import_sliding_tab_layout" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" /> + android:layout_height="wrap_content" /> + + <android.support.v4.view.ViewPager + android:id="@+id/import_pager" + android:layout_width="match_parent" + android:layout_height="64dp" + android:background="@android:color/white" /> + + <View + android:layout_width="match_parent" + android:layout_height="2dip" + android:background="?android:attr/listDivider" /> + + <View + android:layout_width="match_parent" + android:layout_height="16dp" /> + + <View + android:layout_width="match_parent" + android:layout_height="2dip" + android:background="?android:attr/listDivider" /> <FrameLayout android:id="@+id/import_keys_list_container" android:layout_width="match_parent" android:layout_height="0dp" android:orientation="vertical" - android:paddingTop="8dp" - android:paddingLeft="16dp" - android:paddingRight="16dp" - android:layout_weight="0.9" /> + android:layout_weight="1" + android:background="@android:color/white" /> <LinearLayout android:id="@+id/import_footer" @@ -34,7 +51,8 @@ android:layout_height="wrap_content" android:orientation="vertical" android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingRight="16dp" + android:background="@android:color/white"> <View android:layout_width="match_parent" diff --git a/OpenKeychain/src/main/res/layout/import_keys_clipboard_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_clipboard_fragment.xml deleted file mode 100644 index 739c34fba..000000000 --- a/OpenKeychain/src/main/res/layout/import_keys_clipboard_fragment.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingLeft="16dp" - android:paddingRight="16dp" - android:orientation="horizontal" > - - <com.beardedhen.androidbootstrap.BootstrapButton - android:id="@+id/import_clipboard_button" - android:layout_width="match_parent" - android:layout_height="70dp" - android:text="@string/import_clipboard_button" - bootstrapbutton:bb_icon_left="fa-clipboard" - bootstrapbutton:bb_size="default" - bootstrapbutton:bb_type="default" /> - -</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml index c07d2bb40..b1056dab3 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml @@ -1,21 +1,66 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingLeft="16dp" - android:paddingRight="16dp" android:orientation="vertical"> - <com.beardedhen.androidbootstrap.BootstrapButton + <LinearLayout android:id="@+id/import_keys_file_browse" + android:paddingLeft="8dp" android:layout_width="match_parent" - android:layout_height="70dp" - android:text="@string/filemanager_title_open" - android:contentDescription="@string/filemanager_title_open" - bootstrapbutton:bb_icon_left="fa-folder-open" - bootstrapbutton:bb_size="default" - bootstrapbutton:bb_type="default" /> + android:layout_height="?android:attr/listPreferredItemHeight" + android:clickable="true" + style="@style/SelectableItem" + android:orientation="horizontal"> + + <TextView + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="0dip" + android:layout_height="match_parent" + android:text="@string/filemanager_title_open" + android:layout_weight="1" + android:drawableRight="@drawable/ic_action_collection" + android:drawablePadding="8dp" + android:gravity="center_vertical" /> + + <View + android:layout_width="1dip" + android:layout_height="match_parent" + android:gravity="right" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:background="?android:attr/listDivider" /> + + <ImageButton + android:id="@+id/import_clipboard_button" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:padding="8dp" + android:src="@drawable/ic_action_paste" + android:layout_gravity="center_vertical" + style="@style/SelectableItem" /> + + </LinearLayout> + + <TextView + android:id="@+id/import_qrcode_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:paddingTop="8dp" + android:visibility="gone" /> + + <ProgressBar + android:id="@+id/import_qrcode_progress" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:progress="0" + android:visibility="gone" /> </LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml index ceba0e1ce..bf00b77e7 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml @@ -1,16 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingLeft="16dp" - android:paddingRight="16dp" android:orientation="horizontal"> - <EditText android:id="@+id/import_keybase_query" + android:layout_marginLeft="8dp" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1" @@ -22,17 +18,24 @@ android:lines="1" android:maxLines="1" android:minLines="1" + android:layout_marginRight="8dp" android:layout_gravity="center_vertical" /> - <com.beardedhen.androidbootstrap.BootstrapButton + <View + android:layout_width="1dip" + android:layout_height="match_parent" + android:gravity="right" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:background="?android:attr/listDivider" /> + + <ImageButton android:id="@+id/import_keybase_search" android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="match_parent" + android:padding="16dp" + android:src="@drawable/ic_action_search" android:layout_gravity="center_vertical" - android:layout_marginLeft="8dp" - bootstrapbutton:bb_icon_left="fa-search" - bootstrapbutton:bb_roundedCorners="true" - bootstrapbutton:bb_size="default" - bootstrapbutton:bb_type="default" /> + style="@style/SelectableItem" /> </LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml b/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml index c91335a5b..56f34e2eb 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml @@ -17,9 +17,13 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" android:orientation="horizontal" - android:paddingRight="?android:attr/scrollbarSize" - android:singleLine="true"> + android:singleLine="true" + android:paddingLeft="8dp" + android:paddingRight="16dp" + android:paddingTop="4dp" + android:paddingBottom="4dp"> <CheckBox android:id="@+id/selected" diff --git a/OpenKeychain/src/main/res/layout/import_keys_nfc_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_nfc_fragment.xml deleted file mode 100644 index 8c0a80e4e..000000000 --- a/OpenKeychain/src/main/res/layout/import_keys_nfc_fragment.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingLeft="16dp" - android:paddingRight="16dp" - android:orientation="horizontal"> - - <com.beardedhen.androidbootstrap.BootstrapButton - android:id="@+id/import_nfc_button" - android:layout_width="wrap_content" - android:layout_height="70dp" - android:layout_alignParentRight="true" - android:layout_alignParentTop="true" - android:layout_marginLeft="8dp" - android:text="@string/import_nfc_help_button" - bootstrapbutton:bb_icon_left="fa-question" - bootstrapbutton:bb_size="default" - bootstrapbutton:bb_type="default" /> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_toLeftOf="@+id/import_nfc_button" - android:text="@string/import_nfc_text" /> - -</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml index 590f7f797..09a31b4a8 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml @@ -1,21 +1,48 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" - android:layout_width="fill_parent" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingLeft="16dp" - android:paddingRight="16dp" android:orientation="vertical"> - <com.beardedhen.androidbootstrap.BootstrapButton + <LinearLayout android:id="@+id/import_qrcode_button" + android:paddingLeft="8dp" android:layout_width="match_parent" - android:layout_height="70dp" - android:text="@string/import_qr_scan_button" - bootstrapbutton:bb_icon_left="fa-barcode" - bootstrapbutton:bb_size="default" - bootstrapbutton:bb_type="default" /> + android:layout_height="?android:attr/listPreferredItemHeight" + android:clickable="true" + style="@style/SelectableItem" + android:orientation="horizontal"> + + <TextView + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="0dip" + android:layout_height="match_parent" + android:text="@string/import_qr_code_button" + android:layout_weight="1" + android:drawableRight="@drawable/ic_action_qr_code" + android:drawablePadding="8dp" + android:gravity="center_vertical" /> + + <View + android:layout_width="1dip" + android:layout_height="match_parent" + android:gravity="right" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:background="?android:attr/listDivider" /> + + <Button + android:id="@+id/import_nfc_button" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:padding="8dp" + android:text="NFC?" + android:layout_gravity="center_vertical" + style="@style/SelectableItem" /> + + </LinearLayout> <TextView android:id="@+id/import_qrcode_text" diff --git a/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml index e17dbe783..7562eaa9b 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml @@ -1,20 +1,13 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" + xmlns:custom="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingTop="8dp" - android:paddingLeft="16dp" - android:paddingRight="16dp" + android:layout_height="wrap_content" android:orientation="vertical"> - <Spinner - android:id="@+id/import_server_spinner" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - <LinearLayout android:layout_width="match_parent" - android:layout_height="wrap_content" + android:paddingLeft="8dp" + android:layout_height="?android:attr/listPreferredItemHeight" android:orientation="horizontal"> <EditText @@ -30,18 +23,63 @@ android:lines="1" android:maxLines="1" android:minLines="1" + android:layout_marginRight="8dp" android:layout_gravity="center_vertical" /> - <com.beardedhen.androidbootstrap.BootstrapButton + <View + android:layout_width="1dip" + android:layout_height="match_parent" + android:gravity="right" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:background="?android:attr/listDivider" /> + + <ImageButton android:id="@+id/import_server_search" android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="match_parent" + android:padding="16dp" + android:src="@drawable/ic_action_search" + android:layout_gravity="center_vertical" + style="@style/SelectableItem" /> + + <View + android:layout_width="1dip" + android:layout_height="match_parent" + android:gravity="right" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:background="?android:attr/listDivider" /> + + <ImageButton + android:id="@+id/import_server_config_button" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:padding="8dp" + android:src="@drawable/ic_action_settings" android:layout_gravity="center_vertical" + style="@style/SelectableItem" /> + + </LinearLayout> + + <LinearLayout + android:id="@+id/import_server_config" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <View + android:layout_width="match_parent" + android:layout_height="1dip" + android:background="?android:attr/listDivider" /> + + <Spinner + android:id="@+id/import_server_spinner" android:layout_marginLeft="8dp" - bootstrapbutton:bb_icon_left="fa-search" - bootstrapbutton:bb_roundedCorners="true" - bootstrapbutton:bb_size="default" - bootstrapbutton:bb_type="default" /> + android:layout_marginRight="8dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/view_key_share_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_share_fragment.xml index 67c2e241a..1cd2b9f1b 100644 --- a/OpenKeychain/src/main/res/layout/view_key_share_fragment.xml +++ b/OpenKeychain/src/main/res/layout/view_key_share_fragment.xml @@ -24,8 +24,7 @@ <LinearLayout android:id="@+id/view_key_action_fingerprint_share" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeight" + android:layout_height="?android:attr/listPreferredItemHeight" android:clickable="true" style="@style/SelectableItem" android:orientation="horizontal"> @@ -63,7 +62,6 @@ </LinearLayout> - <View android:layout_width="match_parent" android:layout_height="1dip" @@ -90,8 +88,7 @@ <LinearLayout android:id="@+id/view_key_action_key_share" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeight" + android:layout_height="?android:attr/listPreferredItemHeight" android:clickable="true" style="@style/SelectableItem" android:orientation="horizontal"> @@ -135,8 +132,7 @@ <LinearLayout android:id="@+id/view_key_action_nfc_help" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeight" + android:layout_height="?android:attr/listPreferredItemHeight" android:clickable="true" style="@style/SelectableItem" android:orientation="horizontal" diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml index 3485846ad..d5cf037c8 100644 --- a/OpenKeychain/src/main/res/values-de/strings.xml +++ b/OpenKeychain/src/main/res/values-de/strings.xml @@ -324,10 +324,8 @@ <string name="progress_verifying_integrity">Integrität wird überprüft…</string> <string name="progress_deleting_securely">\'%s\' wird sicher gelöscht…</string> <!--action strings--> - <string name="hint_public_keys">Öffentliche Schlüssel suchen</string> <string name="hint_secret_keys">Private Schlüssel suchen</string> <string name="action_share_key_with">Teile Schlüssel über…</string> - <string name="hint_keybase_search">Durchsuche Keybase.io</string> <!--key bit length selections--> <string name="key_size_512">512</string> <string name="key_size_768">768</string> diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index bf3333458..3221cd9cd 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -75,16 +75,11 @@ <!-- menu --> <string name="menu_preferences">Settings</string> <string name="menu_help">Help</string> - <string name="menu_import_from_file">Import from file</string> - <string name="menu_import_from_qr_code">Import from QR Code</string> - <string name="menu_import_from_nfc">Import from NFC</string> <string name="menu_export_key">Export to file</string> <string name="menu_delete_key">Delete key</string> <string name="menu_create_key">Create key</string> <string name="menu_create_key_expert">Create key (expert)</string> <string name="menu_search">Search</string> - <string name="menu_import_from_key_server">Keyserver</string> - <string name="menu_import_from_keybase">Import from Keybase.io</string> <string name="menu_key_server">Keyserver…</string> <string name="menu_update_key">Update from keyserver</string> <string name="menu_export_key_to_server">Upload to key server</string> @@ -229,11 +224,6 @@ <string name="key_creation_weak_rsa_info">Note: generating RSA key with length 1024-bit and less is considered unsafe and it\'s disabled for generating new keys.</string> <string name="key_not_found">Couldn\'t find key %08X.</string> - <plurals name="keys_found"> - <item quantity="one">Found %d key.</item> - <item quantity="other">Found %d keys.</item> - </plurals> - <plurals name="bad_keys_encountered"> <item quantity="one">%d bad secret key ignored. Perhaps you exported with the option\n --export-secret-subkeys\nMake sure you export with\n --export-secret-keys\ninstead.</item> <item quantity="other">%d bad secret keys ignored. Perhaps you exported with the option\n --export-secret-subkeys\nMake sure you export with\n --export-secret-keys\ninstead.</item> @@ -278,11 +268,11 @@ <string name="error_only_files_are_supported">Direct binary data without actual file in filesystem is not supported.</string> <string name="error_jelly_bean_needed">You need Android 4.1 to use Android\'s NFC Beam feature!</string> <string name="error_nfc_needed">NFC is not available on your device!</string> - <string name="error_nothing_import">Nothing to import!</string> + <string name="error_nothing_import">No keys found!</string> <string name="error_keyserver_insufficient_query">Key search query too short</string> <string name="error_searching_keys">Unrecoverable error searching for keys at server</string> <string name="error_keyserver_too_many_responses">Key search query returned too many candidates; Please refine query</string> - <string name="error_import_file_no_content">File has no content</string> + <string name="error_import_file_no_content">File/Clipboard is empty</string> <string name="error_generic_report_bug">A generic error occurred, please create a new bug report for OpenKeychain.</string> <plurals name="error_import_non_pgp_part"> <item quantity="one">part of the loaded file is a valid OpenPGP object but not a OpenPGP key</item> @@ -339,10 +329,10 @@ <string name="progress_deleting_securely">deleting \'%s\' securely…</string> <!-- action strings --> - <string name="hint_public_keys">Search Public Keys</string> + <string name="hint_public_keys">Name/Email/Key ID…</string> <string name="hint_secret_keys">Search Secret Keys</string> <string name="action_share_key_with">Share Key with…</string> - <string name="hint_keybase_search">Search Keybase.io</string> + <string name="hint_keybase_search">Name/Keybase.io username…</string> <!-- key bit length selections --> <string name="key_size_512">512</string> @@ -372,6 +362,10 @@ <string name="help_about_version">Version:</string> <!-- Import --> + <string name="import_tab_keyserver">Keyserver</string> + <string name="import_tab_direct">File/Clipboard</string> + <string name="import_tab_qr_code">QR Code/NFC</string> + <string name="import_tab_keybase">Keybase.io</string> <string name="import_import">Import selected keys</string> <string name="import_from_clipboard">Import from clipboard</string> @@ -387,6 +381,7 @@ <string name="import_qr_scan_button">Scan QR Code with \'Barcode Scanner\'</string> <string name="import_nfc_text">To receive keys via NFC, the device needs to be unlocked.</string> <string name="import_nfc_help_button">Help</string> + <string name="import_qr_code_button">Scan QR Code…</string> <string name="import_clipboard_button">Get key from clipboard</string> <string name="import_keybase_button">Get key from Keybase.io</string> @@ -512,13 +507,12 @@ <string name="msg_ip_delete_old_ok">Deleted old key from database</string> <string name="msg_ip_encode_fail">Operation failed due to encoding error</string> <string name="msg_ip_fail_io_exc">Operation failed due to i/o error</string> - <string name="msg_ip_fail_op_ex">Operation failed due to database error</string> + <string name="msg_ip_fail_op_exc">Operation failed due to database error</string> <string name="msg_ip_fail_remote_ex">Operation failed due to internal error</string> <string name="msg_ip">Importing public keyring %s</string> <string name="msg_ip_insert_keyring">Encoding keyring data</string> <string name="msg_ip_insert_keys">Parsing keys</string> <string name="msg_ip_prepare">Preparing database operations</string> - <string name="msg_ip_preserving_secret">Preserving available secret key</string> <string name="msg_ip_master">Processing master key %s</string> <string name="msg_ip_master_expired">Keyring expired on %s</string> <string name="msg_ip_master_expires">Keyring expires on %s</string> @@ -542,6 +536,7 @@ <string name="msg_ip_subkey_flags_xxs">Subkey flags: sign</string> <string name="msg_ip_subkey_flags_xxx">Subkey flags: none</string> <string name="msg_ip_success">Successfully imported public keyring</string> + <string name="msg_ip_success_identical">Keyring contains no new data, nothing to do</string> <string name="msg_ip_reinsert_secret">Re-inserting secret key</string> <string name="msg_ip_uid_cert_bad">Encountered bad certificate!</string> <string name="msg_ip_uid_cert_error">Error processing certificate!</string> @@ -555,15 +550,19 @@ <!-- Import Secret log entries --> <string name="msg_is">Importing secret key %s</string> + <string name="msg_is_db_exception">Database error!</string> <string name="msg_is_importing_subkeys">Processing secret subkeys</string> - <string name="msg_is_io_excption">Error encoding keyring</string> + <string name="msg_is_io_exc">Error encoding keyring</string> + <string name="msg_is_pubring_generate">Generating public keyring from secret keyring</string> <string name="msg_is_subkey_nonexistent">Subkey %s unavailable in public key</string> <string name="msg_is_subkey_ok">Marked %s as available</string> <string name="msg_is_subkey_stripped">Marked %s as stripped</string> + <string name="msg_is_success_identical">Keyring contains no new data, nothing to do</string> <string name="msg_is_success">Successfully imported secret keyring</string> <!-- Keyring Canonicalization log entries --> - <string name="msg_kc">Canonicalizing keyring %s</string> + <string name="msg_kc_public">Canonicalizing public keyring %s</string> + <string name="msg_kc_secret">Canonicalizing secret keyring %s</string> <string name="msg_kc_fatal_no_uid">Keyring canonicalization failed: Keyring has no valid user ids</string> <string name="msg_kc_master">Processing master key</string> <string name="msg_kc_revoke_bad_err">Removing bad keyring revocation certificate</string> @@ -586,7 +585,7 @@ <string name="msg_kc_sub_revoke_bad_err">Removing bad subkey revocation key</string> <string name="msg_kc_sub_revoke_bad">Removing bad subkey revocation key</string> <string name="msg_kc_sub_revoke_dup">Removing redundant keyring revocation key</string> - <string name="msg_kc_success">Keyring canonicalization successful</string> + <string name="msg_kc_success">Keyring canonicalization successful, no changes</string> <string name="msg_kc_success_bad">Keyring canonicalization successful, removed %s erroneous certificates</string> <string name="msg_kc_success_bad_and_red">Keyring canonicalization successful, removed %1$s erroneous and %2$s redundant certificates</string> <string name="msg_kc_success_redundant">Keyring canonicalization successful, removed %s redundant certificates</string> @@ -596,28 +595,37 @@ <string name="msg_kc_uid_bad_type">Removing user id certificate of unknown type (%s)</string> <string name="msg_kc_uid_bad">Removing bad self certificate for user id "%s"</string> <string name="msg_kc_uid_dup">Removing outdated self certificate for user id "%s"</string> + <string name="msg_kc_uid_foreign">Removing foreign user id certificate by %s</string> <string name="msg_kc_uid_revoke_dup">Removing redundant revocation certificate for user id "%s"</string> <string name="msg_kc_uid_revoke_old">Removing outdated revocation certificate for user id "%s"</string> <string name="msg_kc_uid_no_cert">No valid self-certificate found for user id %s, removing from ring</string> + <!-- Keyring merging log entries --> + <string name="msg_mg_public">Merging into public keyring %s</string> + <string name="msg_mg_secret">Merging into secret keyring %s</string> + <string name="msg_mg_fatal_encode">Fatal error encoding signature</string> + <string name="msg_mg_heterogeneous">Tried to consolidate heterogeneous keyrings</string> + <string name="msg_mg_new_subkey">Adding new subkey %s</string> + <string name="msg_mg_found_new">Found %s new certificates in keyring</string> + <!-- modifySecretKeyRing --> <string name="msg_mr">Modifying keyring %s</string> - <string name="msg_mr_error_encode">Encoding exception!</string> - <string name="msg_mr_error_pgp">PGP internal exception!</string> - <string name="msg_mr_error_sig">Signature exception!</string> - <string name="msg_mr_passphrase">Changing passphrase</string> - <string name="msg_mr_subkey_change">Modifying subkey %s</string> - <string name="msg_mr_subkey_missing">Tried to operate on missing subkey %s!</string> - <string name="msg_mr_subkey_new">Generating new %1$s bit %2$s subkey</string> - <string name="msg_mr_subkey_new_id">New subkey id: %s</string> - <string name="msg_mr_subkey_past_expiry">Expiry date cannot be in the past!</string> - <string name="msg_mr_subkey_revoke">Revoking subkey %s</string> - <string name="msg_mr_success">Keyring successfully modified</string> - <string name="msg_mr_uid_add">Adding user id %s</string> - <string name="msg_mr_uid_primary">Changing primary uid to %s</string> - <string name="msg_mr_uid_revoke">Revoking user id %s</string> - <string name="msg_mr_unlock_error">Error unlocking keyring!</string> - <string name="msg_mr_unlock">Unlocking keyring</string> + <string name="msg_mf_error_encode">Encoding exception!</string> + <string name="msg_mf_error_pgp">PGP internal exception!</string> + <string name="msg_mf_error_sig">Signature exception!</string> + <string name="msg_mf_passphrase">Changing passphrase</string> + <string name="msg_mf_subkey_change">Modifying subkey %s</string> + <string name="msg_mf_subkey_missing">Tried to operate on missing subkey %s!</string> + <string name="msg_mf_subkey_new">Generating new %1$s bit %2$s subkey</string> + <string name="msg_mf_subkey_new_id">New subkey id: %s</string> + <string name="msg_mf_subkey_past_expiry">Expiry date cannot be in the past!</string> + <string name="msg_mf_subkey_revoke">Revoking subkey %s</string> + <string name="msg_mf_success">Keyring successfully modified</string> + <string name="msg_mf_uid_add">Adding user id %s</string> + <string name="msg_mf_uid_primary">Changing primary uid to %s</string> + <string name="msg_mf_uid_revoke">Revoking user id %s</string> + <string name="msg_mf_unlock_error">Error unlocking keyring!</string> + <string name="msg_mf_unlock">Unlocking keyring</string> <!-- unsorted --> <string name="section_certifier_id">Certifier</string> |