diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure')
84 files changed, 1533 insertions, 1005 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 30d855a74..fc1cb8acc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -42,6 +42,15 @@ public final class Constants { // as defined in http://tools.ietf.org/html/rfc3156, section 7 public static final String NFC_MIME = "application/pgp-keys"; + // as defined in http://tools.ietf.org/html/rfc3156 + // we don't use application/pgp-encrypted as it only holds the version number + public static final String ENCRYPTED_FILES_MIME = "application/octet-stream"; + public static final String ENCRYPTED_TEXT_MIME = "text/plain"; + + public static final String FILE_EXTENSION_PGP_MAIN = ".gpg"; + public static final String FILE_EXTENSION_PGP_ALTERNATE = ".pgp"; + public static final String FILE_EXTENSION_ASC = ".asc"; + // used by QR Codes (Guardian Project, Monkeysphere compatiblity) public static final String FINGERPRINT_SCHEME = "openpgp4fpr"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index a4dc12a37..d26ccbe57 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -118,24 +118,21 @@ public class KeychainApplication extends Application { * @param context */ public static void setupAccountAsNeeded(Context context) { - // only enabled for Jelly Bean because we need some newer methods in our sync adapter - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - try { - AccountManager manager = AccountManager.get(context); - Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE); - if (accounts == null || accounts.length == 0) { - Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE); - if (manager.addAccountExplicitly(account, null, null)) { - ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1); - ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true); - } else { - Log.e(Constants.TAG, "Adding account failed!"); - } + try { + AccountManager manager = AccountManager.get(context); + Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE); + if (accounts == null || accounts.length == 0) { + Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE); + if (manager.addAccountExplicitly(account, null, null)) { + ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1); + ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true); + } else { + Log.e(Constants.TAG, "Adding account failed!"); } - } catch (SecurityException e) { - Log.e(Constants.TAG, "SecurityException when adding the account", e); - Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show(); } + } catch (SecurityException e) { + Log.e(Constants.TAG, "SecurityException when adding the account", e); + Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java index 02cb502d0..03439228b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java @@ -70,6 +70,8 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> { modified = true; } + // keep track if this key result is from a HKP keyserver + boolean incomingFromHkpServer = true; // we’re going to want to try to fetch the key from everywhere we found it, so remember // all the origins for (String origin : incoming.getOrigins()) { @@ -78,13 +80,24 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> { // to work properly, Keybase-sourced entries need to pass along the extra if (KeybaseKeyserver.ORIGIN.equals(origin)) { existing.setExtraData(incoming.getExtraData()); + // one of the origins is not a HKP keyserver + incomingFromHkpServer = false; } } + ArrayList<String> incomingIDs = incoming.getUserIds(); ArrayList<String> existingIDs = existing.getUserIds(); for (String incomingID : incomingIDs) { if (!existingIDs.contains(incomingID)) { - existingIDs.add(incomingID); + // prepend HKP server results to the start of the list, + // so that the UI (for cloud key search, which is picking the first list item) + // shows the right main email address, as mail addresses returned by HKP servers + // are preferred over keybase.io IDs + if (incomingFromHkpServer) { + existingIDs.add(0, incomingID); + } else { + existingIDs.add(incomingID); + } modified = true; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index 7dac8b1e0..bb86d272f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -307,24 +307,22 @@ public class ImportKeysListEntry implements Serializable, Parcelable { public void updateMergedUserIds() { mMergedUserIds = new HashMap<>(); for (String userId : mUserIds) { - String[] userIdSplit = KeyRing.splitUserId(userId); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); // TODO: comment field? - // name - if (userIdSplit[0] != null) { - // email - if (userIdSplit[1] != null) { - if (!mMergedUserIds.containsKey(userIdSplit[0])) { + if (userIdSplit.name != null) { + if (userIdSplit.email != null) { + if (!mMergedUserIds.containsKey(userIdSplit.name)) { HashSet<String> emails = new HashSet<>(); - emails.add(userIdSplit[1]); - mMergedUserIds.put(userIdSplit[0], emails); + emails.add(userIdSplit.email); + mMergedUserIds.put(userIdSplit.name, emails); } else { - mMergedUserIds.get(userIdSplit[0]).add(userIdSplit[1]); + mMergedUserIds.get(userIdSplit.name).add(userIdSplit.email); } } else { // name only - mMergedUserIds.put(userIdSplit[0], new HashSet<String>()); + mMergedUserIds.put(userIdSplit.name, new HashSet<String>()); } } else { // fallback diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java index 40dcbd78d..a824e73d7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java @@ -24,6 +24,7 @@ import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.concurrent.atomic.AtomicBoolean; @@ -101,7 +102,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface { } @Override - public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException { + public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException { try { long masterKeyId = mProviderHelper.getMasterKeyId(subKeyId); return getCachedPassphrase(masterKeyId, subKeyId); @@ -111,7 +112,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface { } @Override - public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException { + public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException { try { return PassphraseCacheService.getCachedPassphrase( mContext, masterKeyId, subKeyId); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java index ebf0dc70b..4ceb34722 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyActio import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; @@ -79,7 +80,7 @@ public class CertifyOperation extends BaseOperation { } // certification is always with the master key id, so use that one - String passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId); + Passphrase passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId); if (!certificationKey.unlock(passphrase)) { log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java index bcd842dd0..a179b53ee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java @@ -35,6 +35,7 @@ import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import java.util.concurrent.atomic.AtomicBoolean; @@ -55,7 +56,7 @@ public class EditKeyOperation extends BaseOperation { super(context, providerHelper, progressable, cancelled); } - public EditKeyResult execute(SaveKeyringParcel saveParcel, String passphrase) { + public EditKeyResult execute(SaveKeyringParcel saveParcel, Passphrase passphrase) { OperationLog log = new OperationLog(); log.add(LogType.MSG_ED, 0); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java index 94684851a..f56fe4bb9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java @@ -120,7 +120,7 @@ public class CertifyResult extends OperationResult { mCertifyError, mCertifyError); } - return Notify.createNotify(activity, str, duration, style, new ActionListener() { + return Notify.create(activity, str, duration, style, new ActionListener() { @Override public void onAction() { Intent intent = new Intent( diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java index 86b37fea6..7df37cd9b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java @@ -22,6 +22,7 @@ import android.os.Parcel; import org.openintents.openpgp.OpenPgpMetadata; import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.util.Passphrase; public class DecryptVerifyResult extends OperationResult { @@ -37,7 +38,7 @@ public class DecryptVerifyResult extends OperationResult { long mNfcSubKeyId; byte[] mNfcSessionKey; - String mNfcPassphrase; + Passphrase mNfcPassphrase; OpenPgpSignatureResult mSignatureResult; OpenPgpMetadata mDecryptMetadata; @@ -53,7 +54,7 @@ public class DecryptVerifyResult extends OperationResult { mKeyIdPassphraseNeeded = keyIdPassphraseNeeded; } - public void setNfcState(long subKeyId, byte[] sessionKey, String passphrase) { + public void setNfcState(long subKeyId, byte[] sessionKey, Passphrase passphrase) { mNfcSubKeyId = subKeyId; mNfcSessionKey = sessionKey; mNfcPassphrase = passphrase; @@ -67,7 +68,7 @@ public class DecryptVerifyResult extends OperationResult { return mNfcSessionKey; } - public String getNfcPassphrase() { + public Passphrase getNfcPassphrase() { return mNfcPassphrase; } @@ -109,7 +110,7 @@ public class DecryptVerifyResult extends OperationResult { mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader()); mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader()); mNfcSessionKey = source.readInt() != 0 ? source.createByteArray() : null; - mNfcPassphrase = source.readString(); + mNfcPassphrase = source.readParcelable(Passphrase.class.getClassLoader()); } public int describeContents() { @@ -127,7 +128,7 @@ public class DecryptVerifyResult extends OperationResult { } else { dest.writeInt(0); } - dest.writeString(mNfcPassphrase); + dest.writeParcelable(mNfcPassphrase, flags); } public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java index 62197541a..50f49add2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java @@ -116,7 +116,7 @@ public class DeleteResult extends OperationResult { } } - return Notify.createNotify(activity, str, duration, style, new ActionListener() { + return Notify.create(activity, str, duration, style, new ActionListener() { @Override public void onAction() { Intent intent = new Intent( diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java index 2d533ed16..af9f67114 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java @@ -179,7 +179,7 @@ public class ImportKeyResult extends OperationResult { } } - return Notify.createNotify(activity, str, duration, style, new ActionListener() { + return Notify.create(activity, str, duration, style, new ActionListener() { @Override public void onAction() { Intent intent = new Intent( diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 7cb5e4904..616b6f062 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -273,19 +273,19 @@ public abstract class OperationResult implements Parcelable { } if (getLog() == null || getLog().isEmpty()) { - return Notify.createNotify(activity, msgId, Notify.LENGTH_LONG, style); - } - - return Notify.createNotify(activity, msgId, Notify.LENGTH_LONG, style, - new ActionListener() { - @Override - public void onAction() { - Intent intent = new Intent( - activity, LogDisplayActivity.class); - intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this); - activity.startActivity(intent); - } - }, R.string.view_log); + return Notify.create(activity, msgId, Notify.LENGTH_LONG, style); + } + + return Notify.create(activity, msgId, Notify.LENGTH_LONG, style, + new ActionListener() { + @Override + public void onAction() { + Intent intent = new Intent( + activity, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this); + activity.startActivity(intent); + } + }, R.string.view_log); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java index de2f64404..cf40001b3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java @@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.operations.results; import android.os.Parcel; +import org.sufficientlysecure.keychain.util.Passphrase; + import java.util.Date; public class PgpSignEncryptResult extends OperationResult { @@ -36,7 +38,7 @@ public class PgpSignEncryptResult extends OperationResult { byte[] mNfcHash; int mNfcAlgo; Date mNfcTimestamp; - String mNfcPassphrase; + Passphrase mNfcPassphrase; byte[] mDetachedSignature; public long getKeyIdPassphraseNeeded() { @@ -47,7 +49,7 @@ public class PgpSignEncryptResult extends OperationResult { mKeyIdPassphraseNeeded = keyIdPassphraseNeeded; } - public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, String passphrase) { + public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, Passphrase passphrase) { mNfcKeyId = nfcKeyId; mNfcHash = nfcHash; mNfcAlgo = nfcAlgo; @@ -75,7 +77,7 @@ public class PgpSignEncryptResult extends OperationResult { return mNfcTimestamp; } - public String getNfcPassphrase() { + public Passphrase getNfcPassphrase() { return mNfcPassphrase; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java index 8104c5249..412468a48 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java @@ -55,8 +55,8 @@ public class CanonicalizedPublicKey extends UncachedPublicKey { return new IterableIterator<String>(mPublicKey.getUserIDs()); } - JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() { - return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey); + JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator(boolean hiddenRecipients) { + return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey, hiddenRecipients); } public boolean canSign() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index ab91d7747..6ce77394c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; import java.util.Date; @@ -149,7 +150,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { /** * Returns true on right passphrase */ - public boolean unlock(String passphrase) throws PgpGeneralException { + public boolean unlock(Passphrase passphrase) throws PgpGeneralException { // handle keys on OpenPGP cards like they were unlocked if (mSecretKey.getS2K() != null && mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K @@ -161,7 +162,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { // try to extract keys using the passphrase try { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray()); mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor); mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED; } catch (PGPException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java index 26375219b..825795cc6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java @@ -44,7 +44,7 @@ public abstract class KeyRing { abstract public String getPrimaryUserIdWithFallback() throws PgpKeyNotFoundException; - public String[] getSplitPrimaryUserIdWithFallback() throws PgpKeyNotFoundException { + public UserId getSplitPrimaryUserIdWithFallback() throws PgpKeyNotFoundException { return splitUserId(getPrimaryUserIdWithFallback()); } @@ -62,55 +62,45 @@ public abstract class KeyRing { /** * Splits userId string into naming part, email part, and comment part - * - * @param userId - * @return array with naming (0), email (1), comment (2) + * <p/> + * User ID matching: + * http://fiddle.re/t4p6f */ - public static String[] splitUserId(String userId) { - String[] result = new String[]{null, null, null}; - - if (userId == null || userId.equals("")) { - return result; - } - - /* - * User ID matching: - * http://fiddle.re/t4p6f - * - * test cases: - * "Max Mustermann (this is a comment) <max@example.com>" - * "Max Mustermann <max@example.com>" - * "Max Mustermann (this is a comment)" - * "Max Mustermann [this is nothing]" - */ - Matcher matcher = USER_ID_PATTERN.matcher(userId); - if (matcher.matches()) { - result[0] = matcher.group(1); - result[1] = matcher.group(3); - result[2] = matcher.group(2); + public static UserId splitUserId(final String userId) { + if (!TextUtils.isEmpty(userId)) { + final Matcher matcher = USER_ID_PATTERN.matcher(userId); + if (matcher.matches()) { + return new UserId(matcher.group(1), matcher.group(3), matcher.group(2)); + } } - - return result; + return new UserId(null, null, null); } /** * Returns a composed user id. Returns null if name is null! - * - * @param name - * @param email - * @param comment - * @return */ - public static String createUserId(String name, String email, String comment) { - String userId = name; // consider name a required value - if (userId != null && !TextUtils.isEmpty(comment)) { - userId += " (" + comment + ")"; + public static String createUserId(UserId userId) { + String userIdString = userId.name; // consider name a required value + if (userIdString != null && !TextUtils.isEmpty(userId.comment)) { + userIdString += " (" + userId.comment + ")"; } - if (userId != null && !TextUtils.isEmpty(email)) { - userId += " <" + email + ">"; + if (userIdString != null && !TextUtils.isEmpty(userId.email)) { + userIdString += " <" + userId.email + ">"; } - return userId; + return userIdString; + } + + public static class UserId { + public final String name; + public final String email; + public final String comment; + + public UserId(String name, String email, String comment) { + this.name = name; + this.email = email; + this.comment = comment; + } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java index ae1b94a34..88ccccc6a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java @@ -1,13 +1,33 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + package org.sufficientlysecure.keychain.pgp; +import org.sufficientlysecure.keychain.util.Passphrase; + public interface PassphraseCacheInterface { public static class NoSecretKeyException extends Exception { public NoSecretKeyException() { } } - public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException; + public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException; - public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException; + public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index 14bc56538..364a1067d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -60,6 +60,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import java.io.BufferedInputStream; @@ -83,7 +84,7 @@ public class PgpDecryptVerify extends BaseOperation { private OutputStream mOutStream; private boolean mAllowSymmetricDecryption; - private String mPassphrase; + private Passphrase mPassphrase; private Set<Long> mAllowedKeyIds; private boolean mDecryptMetadataOnly; private byte[] mDecryptedSessionKey; @@ -118,7 +119,7 @@ public class PgpDecryptVerify extends BaseOperation { private OutputStream mOutStream = null; private Progressable mProgressable = null; private boolean mAllowSymmetricDecryption = true; - private String mPassphrase = null; + private Passphrase mPassphrase = null; private Set<Long> mAllowedKeyIds = null; private boolean mDecryptMetadataOnly = false; private byte[] mDecryptedSessionKey = null; @@ -159,7 +160,7 @@ public class PgpDecryptVerify extends BaseOperation { return this; } - public Builder setPassphrase(String passphrase) { + public Builder setPassphrase(Passphrase passphrase) { mPassphrase = passphrase; return this; } @@ -572,7 +573,7 @@ public class PgpDecryptVerify extends BaseOperation { .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(); PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - mPassphrase.toCharArray()); + mPassphrase.getCharArray()); clear = encryptedDataSymmetric.getDataStream(decryptorFactory); encryptedData = encryptedDataSymmetric; 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 8fb5392e3..b3bf92364 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -57,6 +57,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -316,7 +317,7 @@ public class PgpKeyOperation { masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator()); subProgressPush(50, 100); - return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log); + return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, new Passphrase(), log); } catch (PGPException e) { log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent); @@ -348,7 +349,7 @@ public class PgpKeyOperation { * */ public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, - String passphrase) { + Passphrase passphrase) { OperationLog log = new OperationLog(); int indent = 0; @@ -404,7 +405,7 @@ public class PgpKeyOperation { private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey, int masterKeyFlags, long masterKeyExpiry, - SaveKeyringParcel saveParcel, String passphrase, + SaveKeyringParcel saveParcel, Passphrase passphrase, OperationLog log) { int indent = 1; @@ -420,7 +421,7 @@ public class PgpKeyOperation { { try { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray()); masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); } catch (PGPException e) { log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1); @@ -839,7 +840,7 @@ public class PgpKeyOperation { PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray()); PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder() .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO); @@ -967,7 +968,7 @@ public class PgpKeyOperation { PGPSecretKeyRing sKR, PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, - String passphrase, + Passphrase passphrase, ChangeUnlockParcel newUnlock, OperationLog log, int indent) throws PGPException { @@ -1051,20 +1052,20 @@ public class PgpKeyOperation { private static PGPSecretKeyRing applyNewPassphrase( PGPSecretKeyRing sKR, PGPPublicKey masterPublicKey, - String passphrase, - String newPassphrase, + Passphrase passphrase, + Passphrase newPassphrase, OperationLog log, int indent) throws PGPException { PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build() .get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray()); // Build key encryptor based on new passphrase PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - newPassphrase.toCharArray()); + newPassphrase.getCharArray()); // noinspection unchecked for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) { @@ -1295,11 +1296,11 @@ public class PgpKeyOperation { private static PGPSignature generateSubkeyBindingSignature( PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, - PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, String passphrase) + PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, Passphrase passphrase) throws IOException, PGPException, SignatureException { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - passphrase.toCharArray()); + passphrase.getCharArray()); PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor); return generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, subPrivateKey, pKey, flags, expiry); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java index 1ed0a4720..4a920685a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java @@ -1,7 +1,26 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.Date; @@ -11,19 +30,20 @@ public class PgpSignEncryptInput { protected boolean mEnableAsciiArmorOutput = false; protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED; protected long[] mEncryptionMasterKeyIds = null; - protected String mSymmetricPassphrase = null; + protected Passphrase mSymmetricPassphrase = null; protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED; protected long mSignatureMasterKeyId = Constants.key.none; protected Long mSignatureSubKeyId = null; protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED; - protected String mSignaturePassphrase = null; + protected Passphrase mSignaturePassphrase = null; protected long mAdditionalEncryptId = Constants.key.none; protected byte[] mNfcSignedHash = null; protected Date mNfcCreationTimestamp = null; protected boolean mFailOnMissingEncryptionKeyIds = false; protected String mCharset; protected boolean mCleartextSignature; - protected boolean mDetachedSignature; + protected boolean mDetachedSignature = false; + protected boolean mHiddenRecipients = false; public String getCharset() { return mCharset; @@ -33,7 +53,7 @@ public class PgpSignEncryptInput { this.mCharset = mCharset; } - public boolean ismFailOnMissingEncryptionKeyIds() { + public boolean isFailOnMissingEncryptionKeyIds() { return mFailOnMissingEncryptionKeyIds; } @@ -54,11 +74,11 @@ public class PgpSignEncryptInput { return this; } - public String getSignaturePassphrase() { + public Passphrase getSignaturePassphrase() { return mSignaturePassphrase; } - public PgpSignEncryptInput setSignaturePassphrase(String signaturePassphrase) { + public PgpSignEncryptInput setSignaturePassphrase(Passphrase signaturePassphrase) { mSignaturePassphrase = signaturePassphrase; return this; } @@ -99,11 +119,11 @@ public class PgpSignEncryptInput { return this; } - public String getSymmetricPassphrase() { + public Passphrase getSymmetricPassphrase() { return mSymmetricPassphrase; } - public PgpSignEncryptInput setSymmetricPassphrase(String symmetricPassphrase) { + public PgpSignEncryptInput setSymmetricPassphrase(Passphrase symmetricPassphrase) { mSymmetricPassphrase = symmetricPassphrase; return this; } @@ -126,7 +146,7 @@ public class PgpSignEncryptInput { return this; } - public boolean ismEnableAsciiArmorOutput() { + public boolean isEnableAsciiArmorOutput() { return mEnableAsciiArmorOutput; } @@ -172,5 +192,14 @@ public class PgpSignEncryptInput { public boolean isDetachedSignature() { return mDetachedSignature; } + + public PgpSignEncryptInput setHiddenRecipients(boolean hiddenRecipients) { + this.mHiddenRecipients = hiddenRecipients; + return this; + } + + public boolean isHiddenRecipients() { + return mHiddenRecipients; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java index 81cc2c847..bd3c31d3a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org> * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> * @@ -60,7 +60,6 @@ import java.security.SignatureException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.LinkedList; import java.util.concurrent.atomic.AtomicBoolean; /** This class supports a single, low-level, sign/encrypt operation. @@ -117,7 +116,8 @@ public class PgpSignEncryptOperation extends BaseOperation { Log.d(Constants.TAG, "enableSignature:" + enableSignature + "\nenableEncryption:" + enableEncryption + "\nenableCompression:" + enableCompression - + "\nenableAsciiArmorOutput:" + input.ismEnableAsciiArmorOutput()); + + "\nenableAsciiArmorOutput:" + input.isEnableAsciiArmorOutput() + + "\nisHiddenRecipients:" + input.isHiddenRecipients()); // add additional key id to encryption ids (mostly to do self-encryption) if (enableEncryption && input.getAdditionalEncryptId() != Constants.key.none) { @@ -127,7 +127,7 @@ public class PgpSignEncryptOperation extends BaseOperation { ArmoredOutputStream armorOut = null; OutputStream out; - if (input.ismEnableAsciiArmorOutput()) { + if (input.isEnableAsciiArmorOutput()) { armorOut = new ArmoredOutputStream(outputStream); if (input.getVersionHeader() != null) { armorOut.setHeader("Version", input.getVersionHeader()); @@ -243,7 +243,7 @@ public class PgpSignEncryptOperation extends BaseOperation { log.add(LogType.MSG_PSE_SYMMETRIC, indent); JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = - new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().toCharArray()); + new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().getCharArray()); cPk.addMethod(symmetricEncryptionGenerator); } else { log.add(LogType.MSG_PSE_ASYMMETRIC, indent); @@ -254,19 +254,19 @@ public class PgpSignEncryptOperation extends BaseOperation { CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing( KeyRings.buildUnifiedKeyRingUri(id)); CanonicalizedPublicKey key = keyRing.getEncryptionSubKey(); - cPk.addMethod(key.getPubKeyEncryptionGenerator()); + cPk.addMethod(key.getPubKeyEncryptionGenerator(input.isHiddenRecipients())); log.add(LogType.MSG_PSE_KEY_OK, indent + 1, KeyFormattingUtils.convertKeyIdToHex(id)); } catch (PgpKeyNotFoundException e) { log.add(LogType.MSG_PSE_KEY_WARN, indent + 1, KeyFormattingUtils.convertKeyIdToHex(id)); - if (input.ismFailOnMissingEncryptionKeyIds()) { + if (input.isFailOnMissingEncryptionKeyIds()) { return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } } catch (ProviderHelper.NotFoundException e) { log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1, KeyFormattingUtils.convertKeyIdToHex(id)); - if (input.ismFailOnMissingEncryptionKeyIds()) { + if (input.isFailOnMissingEncryptionKeyIds()) { return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); } } @@ -280,7 +280,7 @@ public class PgpSignEncryptOperation extends BaseOperation { updateProgress(R.string.progress_preparing_signature, 4, 100); try { - boolean cleartext = input.isCleartextSignature() && input.ismEnableAsciiArmorOutput() && !enableEncryption; + boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption; signatureGenerator = signingKey.getSignatureGenerator( input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp()); } catch (PgpGeneralException e) { @@ -358,7 +358,7 @@ public class PgpSignEncryptOperation extends BaseOperation { literalGen.close(); indent -= 1; - } else if (enableSignature && input.isCleartextSignature() && input.ismEnableAsciiArmorOutput()) { + } else if (enableSignature && input.isCleartextSignature() && input.isEnableAsciiArmorOutput()) { /* cleartext signature: sign-only of ascii text */ updateProgress(R.string.progress_signing, 8, 100); @@ -404,7 +404,7 @@ public class PgpSignEncryptOperation extends BaseOperation { // handle output stream separately for detached signatures detachedByteOut = new ByteArrayOutputStream(); OutputStream detachedOut = detachedByteOut; - if (input.ismEnableAsciiArmorOutput()) { + if (input.isEnableAsciiArmorOutput()) { detachedArmorOut = new ArmoredOutputStream(detachedOut); if (input.getVersionHeader() != null) { detachedArmorOut.setHeader("Version", input.getVersionHeader()); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java index a4ed33397..975548c95 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java @@ -1,9 +1,29 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + package org.sufficientlysecure.keychain.pgp; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import org.sufficientlysecure.keychain.util.Passphrase; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -39,12 +59,12 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable mEnableAsciiArmorOutput = src.readInt() == 1; mCompressionId = src.readInt(); mEncryptionMasterKeyIds = src.createLongArray(); - mSymmetricPassphrase = src.readString(); + mSymmetricPassphrase = src.readParcelable(Passphrase.class.getClassLoader()); mSymmetricEncryptionAlgorithm = src.readInt(); mSignatureMasterKeyId = src.readLong(); mSignatureSubKeyId = src.readInt() == 1 ? src.readLong() : null; mSignatureHashAlgorithm = src.readInt(); - mSignaturePassphrase = src.readString(); + mSignaturePassphrase = src.readParcelable(Passphrase.class.getClassLoader()); mAdditionalEncryptId = src.readLong(); mNfcSignedHash = src.createByteArray(); mNfcCreationTimestamp = src.readInt() == 1 ? new Date(src.readLong()) : null; @@ -52,6 +72,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable mCharset = src.readString(); mCleartextSignature = src.readInt() == 1; mDetachedSignature = src.readInt() == 1; + mHiddenRecipients = src.readInt() == 1; mInputUris = src.createTypedArrayList(Uri.CREATOR); mOutputUris = src.createTypedArrayList(Uri.CREATOR); @@ -93,7 +114,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0); dest.writeInt(mCompressionId); dest.writeLongArray(mEncryptionMasterKeyIds); - dest.writeString(mSymmetricPassphrase); + dest.writeParcelable(mSymmetricPassphrase, flags); dest.writeInt(mSymmetricEncryptionAlgorithm); dest.writeLong(mSignatureMasterKeyId); if (mSignatureSubKeyId != null) { @@ -103,7 +124,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable dest.writeInt(0); } dest.writeInt(mSignatureHashAlgorithm); - dest.writeString(mSignaturePassphrase); + dest.writeParcelable(mSignaturePassphrase, flags); dest.writeLong(mAdditionalEncryptId); dest.writeByteArray(mNfcSignedHash); if (mNfcCreationTimestamp != null) { @@ -116,6 +137,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable dest.writeString(mCharset); dest.writeInt(mCleartextSignature ? 1 : 0); dest.writeInt(mDetachedSignature ? 1 : 0); + dest.writeInt(mHiddenRecipients ? 1 : 0); dest.writeTypedList(mInputUris); dest.writeTypedList(mOutputUris); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index a4bc95602..bd2866985 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.ViewKeyActivity; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import java.io.IOException; import java.io.InputStream; @@ -179,7 +180,7 @@ public class OpenPgpService extends RemoteService { return result; } - private PendingIntent getNfcSignPendingIntent(Intent data, long keyId, String pin, byte[] hashToSign, int hashAlgo) { + private PendingIntent getNfcSignPendingIntent(Intent data, long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(getBaseContext(), NfcActivity.class); intent.setAction(NfcActivity.ACTION_SIGN_HASH); @@ -195,7 +196,7 @@ public class OpenPgpService extends RemoteService { PendingIntent.FLAG_CANCEL_CURRENT); } - private PendingIntent getNfcDecryptPendingIntent(Intent data, long subKeyId, String pin, byte[] encryptedSessionKey) { + private PendingIntent getNfcDecryptPendingIntent(Intent data, long subKeyId, Passphrase pin, byte[] encryptedSessionKey) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(getBaseContext(), NfcActivity.class); intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY); @@ -239,6 +240,11 @@ public class OpenPgpService extends RemoteService { try { boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + Passphrase passphrase = null; + if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) { + passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)); + } + byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH); if (nfcSignedHash != null) { Log.d(Constants.TAG, "nfcSignedHash:" + Hex.toHexString(nfcSignedHash)); @@ -277,6 +283,7 @@ public class OpenPgpService extends RemoteService { // sign-only PgpSignEncryptInput pseInput = new PgpSignEncryptInput() + .setSignaturePassphrase(passphrase) .setEnableAsciiArmorOutput(asciiArmor) .setCleartextSignature(cleartextSign) .setDetachedSignature(!cleartextSign) @@ -365,6 +372,11 @@ public class OpenPgpService extends RemoteService { compressionId = CompressionAlgorithmTags.UNCOMPRESSED; } + Passphrase passphrase = null; + if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) { + passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)); + } + // first try to get key ids from non-ambiguous key id extra long[] keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS); if (keyIds == null) { @@ -390,7 +402,8 @@ public class OpenPgpService extends RemoteService { InputData inputData = new InputData(is, inputLength, originalFilename); PgpSignEncryptInput pseInput = new PgpSignEncryptInput(); - pseInput.setEnableAsciiArmorOutput(asciiArmor) + pseInput.setSignaturePassphrase(passphrase) + .setEnableAsciiArmorOutput(asciiArmor) .setVersionHeader(null) .setCompressionId(compressionId) .setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED) @@ -498,6 +511,11 @@ public class OpenPgpService extends RemoteService { os = new ParcelFileDescriptor.AutoCloseOutputStream(output); } + Passphrase passphrase = null; + if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) { + passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)); + } + String currentPkg = getCurrentCallingPackage(); Set<Long> allowedKeyIds; if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 7) { @@ -508,7 +526,6 @@ public class OpenPgpService extends RemoteService { KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg)); } - String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); long inputLength = is.available(); InputData inputData = new InputData(is, inputLength); @@ -554,15 +571,16 @@ public class OpenPgpService extends RemoteService { } } else if (pgpResult.success()) { Intent result = new Intent(); - int resultType = OpenPgpApi.RESULT_TYPE_UNENCRYPTED_UNSIGNED; OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult(); + // TODO: currently RESULT_TYPE_UNENCRYPTED_UNSIGNED is never returned + // instead an error is returned when no pgp data has been found + int resultType = OpenPgpApi.RESULT_TYPE_UNENCRYPTED_UNSIGNED; if (signatureResult != null) { resultType |= OpenPgpApi.RESULT_TYPE_SIGNED; if (!signatureResult.isSignatureOnly()) { resultType |= OpenPgpApi.RESULT_TYPE_ENCRYPTED; } - result.putExtra(OpenPgpApi.RESULT_TYPE, resultType); result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult); @@ -582,7 +600,10 @@ public class OpenPgpService extends RemoteService { // If signature key is known, return PendingIntent to show key result.putExtra(OpenPgpApi.RESULT_INTENT, getShowKeyPendingIntent(signatureResult.getKeyId())); } + } else { + resultType |= OpenPgpApi.RESULT_TYPE_ENCRYPTED; } + result.putExtra(OpenPgpApi.RESULT_TYPE, resultType); if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) { OpenPgpMetadata metadata = pgpResult.getDecryptMetadata(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java index 4bb64bcaa..81181d61d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java @@ -92,11 +92,11 @@ public class AccountSettingsFragment extends Fragment { } private void createKey() { - String[] userId = KeyRing.splitUserId(mAccSettings.getAccountName()); + KeyRing.UserId userId = KeyRing.splitUserId(mAccSettings.getAccountName()); Intent intent = new Intent(getActivity(), CreateKeyActivity.class); - intent.putExtra(CreateKeyActivity.EXTRA_NAME, userId[0]); - intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userId[1]); + intent.putExtra(CreateKeyActivity.EXTRA_NAME, userId.name); + intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userId.email); startActivityForResult(intent, REQUEST_CODE_CREATE_KEY); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java index 2c5c78161..e8c3e4511 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java @@ -186,7 +186,7 @@ public class RemoteServiceActivity extends BaseActivity { // user needs to select a key if it has explicitly requested (None is only allowed for new accounts) if (mUpdateExistingAccount && mAccSettingsFragment.getAccSettings().getKeyId() == Constants.key.none) { - Notify.showNotify(RemoteServiceActivity.this, getString(R.string.api_register_error_select_key), Notify.Style.ERROR); + Notify.create(RemoteServiceActivity.this, getString(R.string.api_register_error_select_key), Notify.Style.ERROR).show(); } else { if (mUpdateExistingAccount) { Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(packageName); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java index 5ec47f4c9..98a44466d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java @@ -42,9 +42,11 @@ public class SelectSignKeyIdActivity extends BaseActivity { private Uri mAppUri; private String mPreferredUserId; + private Intent mData; private SelectSignKeyIdListFragment mListFragment; private TextView mActionCreateKey; + private TextView mNone; @Override protected void onCreate(Bundle savedInstanceState) { @@ -67,27 +69,38 @@ public class SelectSignKeyIdActivity extends BaseActivity { createKey(mPreferredUserId); } }); + mNone = (TextView) findViewById(R.id.api_select_sign_key_none); + mNone.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // 0 is "none" + mData.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, 0); + + setResult(Activity.RESULT_OK, mData); + finish(); + } + }); Intent intent = getIntent(); mAppUri = intent.getData(); mPreferredUserId = intent.getStringExtra(EXTRA_USER_ID); - Intent data = intent.getParcelableExtra(EXTRA_DATA); + mData = intent.getParcelableExtra(EXTRA_DATA); if (mAppUri == null) { Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); finish(); return; } else { Log.d(Constants.TAG, "uri: " + mAppUri); - startListFragments(savedInstanceState, mAppUri, data); + startListFragments(savedInstanceState, mAppUri, mData); } } private void createKey(String userId) { - String[] userIdSplit = KeyRing.splitUserId(userId); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); Intent intent = new Intent(this, CreateKeyActivity.class); - intent.putExtra(CreateKeyActivity.EXTRA_NAME, userIdSplit[0]); - intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userIdSplit[1]); + intent.putExtra(CreateKeyActivity.EXTRA_NAME, userIdSplit.name); + intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userIdSplit.email); startActivityForResult(intent, REQUEST_CODE_CREATE_KEY); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 95bd0de35..01b1925a0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -73,6 +73,7 @@ import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache; +import org.sufficientlysecure.keychain.util.Passphrase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -289,7 +290,7 @@ public class KeychainIntentService extends IntentService implements Progressable try { /* Input */ - String passphrase = data.getString(DECRYPT_PASSPHRASE); + Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE); byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); InputData inputData = createDecryptInputData(data); @@ -419,7 +420,7 @@ public class KeychainIntentService extends IntentService implements Progressable try { /* Input */ - String passphrase = data.getString(DECRYPT_PASSPHRASE); + Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE); byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); InputData inputData = createDecryptInputData(data); @@ -477,7 +478,7 @@ public class KeychainIntentService extends IntentService implements Progressable // Input SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); - String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE); + Passphrase passphrase = data.getParcelable(EDIT_KEYRING_PASSPHRASE); // Operation EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java index bd047518d..91a079a5d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java @@ -129,9 +129,9 @@ public class KeychainIntentServiceHandler extends Handler { // show error from service if (data.containsKey(DATA_ERROR)) { - Notify.showNotify(mActivity, + Notify.create(mActivity, mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)), - Notify.Style.ERROR); + Notify.Style.ERROR).show(); } break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 57881f8ee..ee481ad31 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; import java.util.Date; @@ -121,7 +122,7 @@ public class PassphraseCacheService extends Service { * new events to the alarm manager for new passphrases to let them timeout in the future. */ public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId, - String passphrase, + Passphrase passphrase, String primaryUserId) { Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + masterKeyId); @@ -143,7 +144,7 @@ public class PassphraseCacheService extends Service { * @return passphrase or null (if no passphrase is cached for this keyId) */ - public static String getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException { + public static Passphrase getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException { Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() for masterKeyId " + masterKeyId + ", subKeyId " + subKeyId); @@ -190,7 +191,9 @@ public class PassphraseCacheService extends Service { switch (returnMessage.what) { case MSG_PASSPHRASE_CACHE_GET_OKAY: - return returnMessage.getData().getString(EXTRA_PASSPHRASE); + Bundle returnData = returnMessage.getData(); + returnData.setClassLoader(context.getClassLoader()); + return returnData.getParcelable(EXTRA_PASSPHRASE); case MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND: throw new KeyNotFoundException(); default: @@ -202,11 +205,11 @@ public class PassphraseCacheService extends Service { /** * Internal implementation to get cached passphrase. */ - private String getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException { + private Passphrase getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException { // passphrase for symmetric encryption? if (masterKeyId == Constants.key.symmetric) { Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption"); - String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase(); + Passphrase cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase(); if (cachedPassphrase == null) { return null; } @@ -232,13 +235,13 @@ public class PassphraseCacheService extends Service { case DIVERT_TO_CARD: if (Preferences.getPreferences(this).useDefaultYubikeyPin()) { Log.d(Constants.TAG, "PassphraseCacheService: Using default Yubikey PIN: 123456"); - return "123456"; // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/ + return new Passphrase("123456"); // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/ } else { Log.d(Constants.TAG, "PassphraseCacheService: NOT using default Yubikey PIN"); break; } case PASSPHRASE_EMPTY: - return ""; + return new Passphrase(""); case UNAVAILABLE: throw new ProviderHelper.NotFoundException("secret key for this subkey is not available"); case GNU_DUMMY: @@ -331,7 +334,7 @@ public class PassphraseCacheService extends Service { long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1); long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1); - String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE); + Passphrase passphrase = intent.getParcelableExtra(EXTRA_PASSPHRASE); String primaryUserID = intent.getStringExtra(EXTRA_USER_ID); Log.d(Constants.TAG, @@ -373,10 +376,10 @@ public class PassphraseCacheService extends Service { Log.e(Constants.TAG, "PassphraseCacheService: Bad request, missing masterKeyId or subKeyId!"); msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND; } else { - String passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId); + Passphrase passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId); msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY; Bundle bundle = new Bundle(); - bundle.putString(EXTRA_PASSPHRASE, passphrase); + bundle.putParcelable(EXTRA_PASSPHRASE, passphrase); msg.setData(bundle); } } catch (ProviderHelper.NotFoundException e) { @@ -412,7 +415,10 @@ public class PassphraseCacheService extends Service { * Called when one specific passphrase for keyId timed out */ private void timeout(Context context, long keyId) { - // remove passphrase corresponding to keyId from memory + CachedPassphrase cPass = mPassphraseCache.get(keyId); + // clean internal char[] from memory! + cPass.getPassphrase().removeFromMemory(); + // remove passphrase object mPassphraseCache.remove(keyId); Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!"); @@ -517,9 +523,9 @@ public class PassphraseCacheService extends Service { public class CachedPassphrase { private String primaryUserID; - private String passphrase; + private Passphrase passphrase; - public CachedPassphrase(String passphrase, String primaryUserID) { + public CachedPassphrase(Passphrase passphrase, String primaryUserID) { setPassphrase(passphrase); setPrimaryUserID(primaryUserID); } @@ -528,7 +534,7 @@ public class PassphraseCacheService extends Service { return primaryUserID; } - public String getPassphrase() { + public Passphrase getPassphrase() { return passphrase; } @@ -536,7 +542,7 @@ public class PassphraseCacheService extends Service { this.primaryUserID = primaryUserID; } - public void setPassphrase(String passphrase) { + public void setPassphrase(Passphrase passphrase) { this.passphrase = passphrase; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index e2d0c03c9..9fd278c13 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -22,6 +22,7 @@ import android.os.Parcel; import android.os.Parcelable; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.util.Passphrase; import java.io.Serializable; import java.util.ArrayList; @@ -296,33 +297,30 @@ public class SaveKeyringParcel implements Parcelable { public static class ChangeUnlockParcel implements Parcelable { // The new passphrase to use - public final String mNewPassphrase; + public final Passphrase mNewPassphrase; // A new pin to use. Must only contain [0-9]+ - public final String mNewPin; + public final Passphrase mNewPin; - public ChangeUnlockParcel(String newPassphrase) { + public ChangeUnlockParcel(Passphrase newPassphrase) { this(newPassphrase, null); } - public ChangeUnlockParcel(String newPassphrase, String newPin) { + public ChangeUnlockParcel(Passphrase newPassphrase, Passphrase newPin) { if (newPassphrase == null && newPin == null) { throw new RuntimeException("Cannot set both passphrase and pin. THIS IS A BUG!"); } - if (newPin != null && !newPin.matches("[0-9]+")) { - throw new RuntimeException("Pin must be numeric digits only. THIS IS A BUG!"); - } mNewPassphrase = newPassphrase; mNewPin = newPin; } public ChangeUnlockParcel(Parcel source) { - mNewPassphrase = source.readString(); - mNewPin = source.readString(); + mNewPassphrase = source.readParcelable(Passphrase.class.getClassLoader()); + mNewPin = source.readParcelable(Passphrase.class.getClassLoader()); } @Override public void writeToParcel(Parcel destination, int flags) { - destination.writeString(mNewPassphrase); - destination.writeString(mNewPin); + destination.writeParcelable(mNewPassphrase, flags); + destination.writeParcelable(mNewPin, flags); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index a2d4a3d7c..b3738851c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -62,6 +62,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; import java.lang.reflect.Method; @@ -168,8 +169,8 @@ public class CertifyKeyFragment extends LoaderFragment @Override public void onClick(View v) { if (mSignMasterKeyId == Constants.key.none) { - Notify.showNotify(getActivity(), getString(R.string.select_key_to_certify), - Notify.Style.ERROR); + Notify.create(getActivity(), getString(R.string.select_key_to_certify), + Notify.Style.ERROR).show(); } else { initiateCertifying(); } @@ -246,14 +247,14 @@ public class CertifyKeyFragment extends LoaderFragment while (!data.isAfterLast()) { long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID); String userId = data.getString(INDEX_USER_ID); - String[] pieces = KeyRing.splitUserId(userId); + KeyRing.UserId pieces = KeyRing.splitUserId(userId); // Two cases: boolean grouped = masterKeyId == lastMasterKeyId; - boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces[0]); + boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces.name); // Remember for next loop - lastName = pieces[0]; + lastName = pieces.name; Log.d(Constants.TAG, Long.toString(masterKeyId, 16) + (grouped ? "grouped" : "not grouped")); @@ -318,7 +319,7 @@ public class CertifyKeyFragment extends LoaderFragment */ private void initiateCertifying() { // get the user's passphrase for this key (if required) - String passphrase; + Passphrase passphrase; try { passphrase = PassphraseCacheService.getCachedPassphrase(getActivity(), mSignMasterKeyId, mSignMasterKeyId); } catch (PassphraseCacheService.KeyNotFoundException e) { @@ -341,7 +342,6 @@ public class CertifyKeyFragment extends LoaderFragment switch (requestCode) { case REQUEST_CODE_PASSPHRASE: { if (resultCode == Activity.RESULT_OK && data != null) { - String passphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); startCertifying(); } return; @@ -360,8 +360,8 @@ public class CertifyKeyFragment extends LoaderFragment // Bail out if there is not at least one user id selected ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions(); if (certifyActions.isEmpty()) { - Notify.showNotify(getActivity(), "No identities selected!", - Notify.Style.ERROR); + Notify.create(getActivity(), "No identities selected!", + Notify.Style.ERROR).show(); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java index 2da5511b8..ab76f693e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java @@ -20,31 +20,74 @@ package org.sufficientlysecure.keychain.ui; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.view.View; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Passphrase; + +import java.util.ArrayList; public class CreateKeyActivity extends BaseActivity { public static final String EXTRA_NAME = "name"; public static final String EXTRA_EMAIL = "email"; + public static final String EXTRA_FIRST_TIME = "first_time"; + public static final String EXTRA_ADDITIONAL_EMAILS = "additional_emails"; + public static final String EXTRA_PASSPHRASE = "passphrase"; - public static enum FragAction { - START, - TO_RIGHT, - TO_LEFT - } + public static final String FRAGMENT_TAG = "currentFragment"; + + String mName; + String mEmail; + ArrayList<String> mAdditionalEmails; + Passphrase mPassphrase; + boolean mFirstTime; + + Fragment mCurrentFragment; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // pass extras into fragment - CreateKeyNameFragment frag = - CreateKeyNameFragment.newInstance( - getIntent().getStringExtra(EXTRA_NAME), - getIntent().getStringExtra(EXTRA_EMAIL) - ); - loadFragment(null, frag, FragAction.START); + // Check whether we're recreating a previously destroyed instance + if (savedInstanceState != null) { + // Restore value of members from saved state + mName = savedInstanceState.getString(EXTRA_NAME); + mEmail = savedInstanceState.getString(EXTRA_EMAIL); + mAdditionalEmails = savedInstanceState.getStringArrayList(EXTRA_ADDITIONAL_EMAILS); + mPassphrase = savedInstanceState.getParcelable(EXTRA_PASSPHRASE); + mFirstTime = savedInstanceState.getBoolean(EXTRA_FIRST_TIME); + + mCurrentFragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); + } else { + // Initialize members with default values for a new instance + mName = getIntent().getStringExtra(EXTRA_NAME); + mEmail = getIntent().getStringExtra(EXTRA_EMAIL); + mFirstTime = getIntent().getBooleanExtra(EXTRA_FIRST_TIME, false); + + // Start with first fragment of wizard + CreateKeyStartFragment frag = CreateKeyStartFragment.newInstance(); + loadFragment(frag, FragAction.START); + } + + if (mFirstTime) { + setTitle(R.string.app_name); + setActionBarIcon(R.drawable.ic_launcher); + mToolbar.setNavigationOnClickListener(null); + } else { + setTitle(R.string.title_manage_my_keys); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putString(EXTRA_NAME, mName); + outState.putString(EXTRA_EMAIL, mEmail); + outState.putStringArrayList(EXTRA_ADDITIONAL_EMAILS, mAdditionalEmails); + outState.putParcelable(EXTRA_PASSPHRASE, mPassphrase); + outState.putBoolean(EXTRA_FIRST_TIME, mFirstTime); } @Override @@ -52,23 +95,23 @@ public class CreateKeyActivity extends BaseActivity { setContentView(R.layout.create_key_activity); } - public void loadFragment(Bundle savedInstanceState, Fragment fragment, FragAction action) { - // However, if we're being restored from a previous state, - // then we don't need to do anything and should return or else - // we could end up with overlapping fragments. - if (savedInstanceState != null) { - return; - } + public static enum FragAction { + START, + TO_RIGHT, + TO_LEFT + } + + public void loadFragment(Fragment fragment, FragAction action) { + mCurrentFragment = fragment; // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); switch (action) { case START: transaction.setCustomAnimations(0, 0); - transaction.replace(R.id.create_key_fragment_container, fragment) - .commitAllowingStateLoss(); + transaction.replace(R.id.create_key_fragment_container, fragment, FRAGMENT_TAG) + .commit(); break; case TO_LEFT: getSupportFragmentManager().popBackStackImmediate(); @@ -77,8 +120,8 @@ public class CreateKeyActivity extends BaseActivity { transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left, R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right); transaction.addToBackStack(null); - transaction.replace(R.id.create_key_fragment_container, fragment) - .commitAllowingStateLoss(); + transaction.replace(R.id.create_key_fragment_container, fragment, FRAGMENT_TAG) + .commit(); break; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java index 66424e012..2e8a1f370 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java @@ -46,16 +46,12 @@ import java.util.List; public class CreateKeyEmailFragment extends Fragment { - public static final String ARG_NAME = "name"; - public static final String ARG_EMAIL = "email"; - CreateKeyActivity mCreateKeyActivity; EmailEditText mEmailEdit; RecyclerView mEmailsRecyclerView; View mBackButton; View mNextButton; - String mName; ArrayList<EmailAdapter.ViewModel> mAdditionalEmailModels; EmailAdapter mEmailAdapter; @@ -63,13 +59,10 @@ public class CreateKeyEmailFragment extends Fragment { /** * Creates new instance of this fragment */ - public static CreateKeyEmailFragment newInstance(String name, String email) { + public static CreateKeyEmailFragment newInstance() { CreateKeyEmailFragment frag = new CreateKeyEmailFragment(); Bundle args = new Bundle(); - args.putString(ARG_NAME, name); - args.putString(ARG_EMAIL, email); - frag.setArguments(args); return frag; @@ -85,7 +78,7 @@ public class CreateKeyEmailFragment extends Fragment { */ private static boolean isEditTextNotEmpty(Context context, EditText editText) { boolean output = true; - if (editText.getText().toString().length() == 0) { + if (editText.getText().length() == 0) { editText.setError(context.getString(R.string.create_key_empty)); editText.requestFocus(); output = false; @@ -106,31 +99,33 @@ public class CreateKeyEmailFragment extends Fragment { mEmailsRecyclerView = (RecyclerView) view.findViewById(R.id.create_key_emails); // initial values - mName = getArguments().getString(ARG_NAME); - String email = getArguments().getString(ARG_EMAIL); - mEmailEdit.setText(email); + mEmailEdit.setText(mCreateKeyActivity.mEmail); // focus empty edit fields - if (email == null) { + if (mCreateKeyActivity.mEmail == null) { mEmailEdit.requestFocus(); } mBackButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT); + mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); } }); mNextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - createKeyCheck(); + nextClicked(); } }); mEmailsRecyclerView.setHasFixedSize(true); mEmailsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); mEmailsRecyclerView.setItemAnimator(new DefaultItemAnimator()); + // initial values mAdditionalEmailModels = new ArrayList<>(); + if (mCreateKeyActivity.mAdditionalEmails != null) { + setAdditionalEmails(mCreateKeyActivity.mAdditionalEmails); + } mEmailAdapter = new EmailAdapter(mAdditionalEmailModels, new View.OnClickListener() { @Override public void onClick(View v) { @@ -171,25 +166,38 @@ public class CreateKeyEmailFragment extends Fragment { mCreateKeyActivity = (CreateKeyActivity) getActivity(); } - private void createKeyCheck() { + private void nextClicked() { if (isEditTextNotEmpty(getActivity(), mEmailEdit)) { + // save state + mCreateKeyActivity.mEmail = mEmailEdit.getText().toString(); + mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails(); - ArrayList<String> emails = new ArrayList<>(); - for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) { - emails.add(holder.toString()); - } + CreateKeyPassphraseFragment frag = CreateKeyPassphraseFragment.newInstance(); + mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); + } + } - CreateKeyPassphraseFragment frag = - CreateKeyPassphraseFragment.newInstance( - mName, - mEmailEdit.getText().toString(), - emails - ); + private ArrayList<String> getAdditionalEmails() { + ArrayList<String> emails = new ArrayList<>(); + for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) { + emails.add(holder.toString()); + } + return emails; + } - mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT); + private void setAdditionalEmails(ArrayList<String> emails) { + for (String email : emails) { + mAdditionalEmailModels.add(new EmailAdapter.ViewModel(email)); } } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + // save state in activity + mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails(); + } + public static class EmailAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private List<ViewModel> mDataset; private View.OnClickListener mFooterOnClickListener; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index ae42c891d..cbe3eecd4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; import java.util.ArrayList; @@ -64,32 +65,15 @@ public class CreateKeyFinalFragment extends Fragment { TextView mEditText; View mEditButton; - public static final String ARG_NAME = "name"; - public static final String ARG_EMAIL = "email"; - public static final String ARG_ADDITIONAL_EMAILS = "emails"; - public static final String ARG_PASSPHRASE = "passphrase"; - - String mName; - String mEmail; - ArrayList<String> mAdditionalEmails; - String mPassphrase; - SaveKeyringParcel mSaveKeyringParcel; /** * Creates new instance of this fragment */ - public static CreateKeyFinalFragment newInstance(String name, String email, - ArrayList<String> additionalEmails, - String passphrase) { + public static CreateKeyFinalFragment newInstance() { CreateKeyFinalFragment frag = new CreateKeyFinalFragment(); Bundle args = new Bundle(); - args.putString(ARG_NAME, name); - args.putString(ARG_EMAIL, email); - args.putStringArrayList(ARG_ADDITIONAL_EMAILS, additionalEmails); - args.putString(ARG_PASSPHRASE, passphrase); - frag.setArguments(args); return frag; @@ -107,17 +91,11 @@ public class CreateKeyFinalFragment extends Fragment { mEditText = (TextView) view.findViewById(R.id.create_key_edit_text); mEditButton = view.findViewById(R.id.create_key_edit_button); - // get args - mName = getArguments().getString(ARG_NAME); - mEmail = getArguments().getString(ARG_EMAIL); - mAdditionalEmails = getArguments().getStringArrayList(ARG_ADDITIONAL_EMAILS); - mPassphrase = getArguments().getString(ARG_PASSPHRASE); - // set values - mNameEdit.setText(mName); - if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) { - String emailText = mEmail + ", "; - Iterator<?> it = mAdditionalEmails.iterator(); + mNameEdit.setText(mCreateKeyActivity.mName); + if (mCreateKeyActivity.mAdditionalEmails != null && mCreateKeyActivity.mAdditionalEmails.size() > 0) { + String emailText = mCreateKeyActivity.mEmail + ", "; + Iterator<?> it = mCreateKeyActivity.mAdditionalEmails.iterator(); while (it.hasNext()) { Object next = it.next(); emailText += next; @@ -127,7 +105,7 @@ public class CreateKeyFinalFragment extends Fragment { } mEmailEdit.setText(emailText); } else { - mEmailEdit.setText(mEmail); + mEmailEdit.setText(mCreateKeyActivity.mEmail); } mCreateButton.setOnClickListener(new View.OnClickListener() { @@ -140,7 +118,7 @@ public class CreateKeyFinalFragment extends Fragment { mBackButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT); + mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); } }); @@ -157,6 +135,12 @@ public class CreateKeyFinalFragment extends Fragment { } @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mCreateKeyActivity = (CreateKeyActivity) getActivity(); + } + + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQUEST_EDIT_KEY: { @@ -186,17 +170,22 @@ public class CreateKeyFinalFragment extends Fragment { Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L)); mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L)); - String userId = KeyRing.createUserId(mName, mEmail, null); + String userId = KeyRing.createUserId( + new KeyRing.UserId(mCreateKeyActivity.mName, mCreateKeyActivity.mEmail, null) + ); mSaveKeyringParcel.mAddUserIds.add(userId); mSaveKeyringParcel.mChangePrimaryUserId = userId; - if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) { - for (String email : mAdditionalEmails) { - String thisUserId = KeyRing.createUserId(mName, email, null); + if (mCreateKeyActivity.mAdditionalEmails != null + && mCreateKeyActivity.mAdditionalEmails.size() > 0) { + for (String email : mCreateKeyActivity.mAdditionalEmails) { + String thisUserId = KeyRing.createUserId( + new KeyRing.UserId(mCreateKeyActivity.mName, email, null) + ); mSaveKeyringParcel.mAddUserIds.add(thisUserId); } } - mSaveKeyringParcel.mNewUnlock = mPassphrase != null - ? new ChangeUnlockParcel(mPassphrase, null) + mSaveKeyringParcel.mNewUnlock = mCreateKeyActivity.mPassphrase != null + ? new ChangeUnlockParcel(mCreateKeyActivity.mPassphrase, null) : null; } } @@ -288,8 +277,8 @@ public class CreateKeyFinalFragment extends Fragment { // TODO: upload operation needs a result! // TODO: then combine these results //if (result.getResult() == OperationResultParcel.RESULT_OK) { - //Notify.showNotify(getActivity(), R.string.key_send_success, - //Notify.Style.INFO); + //Notify.create(getActivity(), R.string.key_send_success, + //Notify.Style.OK).show(); Intent data = new Intent(); data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java index 50a3bd655..7480367bb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java @@ -24,34 +24,26 @@ import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; -import org.sufficientlysecure.keychain.ui.widget.EmailEditText; import org.sufficientlysecure.keychain.ui.widget.NameEditText; public class CreateKeyNameFragment extends Fragment { - public static final String ARG_NAME = "name"; - public static final String ARG_EMAIL = "email"; - CreateKeyActivity mCreateKeyActivity; NameEditText mNameEdit; + View mBackButton; View mNextButton; - String mEmail; - /** * Creates new instance of this fragment */ - public static CreateKeyNameFragment newInstance(String name, String email) { + public static CreateKeyNameFragment newInstance() { CreateKeyNameFragment frag = new CreateKeyNameFragment(); Bundle args = new Bundle(); - args.putString(ARG_NAME, name); - args.putString(ARG_EMAIL, email); frag.setArguments(args); @@ -68,7 +60,7 @@ public class CreateKeyNameFragment extends Fragment { */ private static boolean isEditTextNotEmpty(Context context, EditText editText) { boolean output = true; - if (editText.getText().toString().length() == 0) { + if (editText.getText().length() == 0) { editText.setError(context.getString(R.string.create_key_empty)); editText.requestFocus(); output = false; @@ -79,39 +71,31 @@ public class CreateKeyNameFragment extends Fragment { return output; } - private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) { - boolean output = true; - if (!editText1.getText().toString().equals(editText2.getText().toString())) { - editText2.setError(context.getString(R.string.create_key_passphrases_not_equal)); - editText2.requestFocus(); - output = false; - } else { - editText2.setError(null); - } - - return output; - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.create_key_name_fragment, container, false); mNameEdit = (NameEditText) view.findViewById(R.id.create_key_name); + mBackButton = view.findViewById(R.id.create_key_back_button); mNextButton = view.findViewById(R.id.create_key_next_button); // initial values - String name = getArguments().getString(ARG_NAME); - mEmail = getArguments().getString(ARG_EMAIL); - mNameEdit.setText(name); + mNameEdit.setText(mCreateKeyActivity.mName); // focus empty edit fields - if (name == null) { + if (mCreateKeyActivity.mName == null) { mNameEdit.requestFocus(); } + mBackButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); + } + }); mNextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - createKeyCheck(); + nextClicked(); } }); @@ -124,16 +108,13 @@ public class CreateKeyNameFragment extends Fragment { mCreateKeyActivity = (CreateKeyActivity) getActivity(); } - private void createKeyCheck() { + private void nextClicked() { if (isEditTextNotEmpty(getActivity(), mNameEdit)) { + // save state + mCreateKeyActivity.mName = mNameEdit.getText().toString(); - CreateKeyEmailFragment frag = - CreateKeyEmailFragment.newInstance( - mNameEdit.getText().toString(), - mEmail - ); - - mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT); + CreateKeyEmailFragment frag = CreateKeyEmailFragment.newInstance(); + mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java index 055ea608b..32173edf7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java @@ -21,6 +21,7 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.text.Editable; import android.text.method.HideReturnsTransformationMethod; import android.text.method.PasswordTransformationMethod; import android.view.LayoutInflater; @@ -34,20 +35,13 @@ import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; +import java.util.Arrays; public class CreateKeyPassphraseFragment extends Fragment { - public static final String ARG_NAME = "name"; - public static final String ARG_EMAIL = "email"; - public static final String ARG_ADDITIONAL_EMAILS = "emails"; - - // model - String mName; - String mEmail; - ArrayList<String> mAdditionalEmails; - // view CreateKeyActivity mCreateKeyActivity; PassphraseEditText mPassphraseEdit; @@ -59,15 +53,10 @@ public class CreateKeyPassphraseFragment extends Fragment { /** * Creates new instance of this fragment */ - public static CreateKeyPassphraseFragment newInstance(String name, String email, - ArrayList<String> additionalEmails) { + public static CreateKeyPassphraseFragment newInstance() { CreateKeyPassphraseFragment frag = new CreateKeyPassphraseFragment(); Bundle args = new Bundle(); - args.putString(ARG_NAME, name); - args.putString(ARG_EMAIL, email); - args.putStringArrayList(ARG_ADDITIONAL_EMAILS, additionalEmails); - frag.setArguments(args); return frag; @@ -83,7 +72,7 @@ public class CreateKeyPassphraseFragment extends Fragment { */ private static boolean isEditTextNotEmpty(Context context, EditText editText) { boolean output = true; - if (editText.getText().toString().length() == 0) { + if (editText.getText().length() == 0) { editText.setError(context.getString(R.string.create_key_empty)); editText.requestFocus(); output = false; @@ -95,11 +84,13 @@ public class CreateKeyPassphraseFragment extends Fragment { } private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) { - boolean output = true; - if (!editText1.getText().toString().equals(editText2.getText().toString())) { + Passphrase p1 = new Passphrase(editText1); + Passphrase p2 = new Passphrase(editText2); + boolean output = (p1.equals(p2)); + + if (!output) { editText2.setError(context.getString(R.string.create_key_passphrases_not_equal)); editText2.requestFocus(); - output = false; } else { editText2.setError(null); } @@ -118,9 +109,12 @@ public class CreateKeyPassphraseFragment extends Fragment { mNextButton = view.findViewById(R.id.create_key_next_button); // initial values - mName = getArguments().getString(ARG_NAME); - mEmail = getArguments().getString(ARG_EMAIL); - mAdditionalEmails = getArguments().getStringArrayList(ARG_ADDITIONAL_EMAILS); + // TODO: using String here is unsafe... + if (mCreateKeyActivity.mPassphrase != null) { + mPassphraseEdit.setText(Arrays.toString(mCreateKeyActivity.mPassphrase.getCharArray())); + mPassphraseEditAgain.setText(Arrays.toString(mCreateKeyActivity.mPassphrase.getCharArray())); + } + mPassphraseEdit.requestFocus(); mBackButton.setOnClickListener(new View.OnClickListener() { @Override @@ -131,7 +125,7 @@ public class CreateKeyPassphraseFragment extends Fragment { mNextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - createKeyCheck(); + nextClicked(); } }); mShowPassphrase.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @@ -159,23 +153,19 @@ public class CreateKeyPassphraseFragment extends Fragment { private void back() { hideKeyboard(); - mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT); + mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); } - private void createKeyCheck() { + private void nextClicked() { if (isEditTextNotEmpty(getActivity(), mPassphraseEdit) && areEditTextsEqual(getActivity(), mPassphraseEdit, mPassphraseEditAgain)) { - CreateKeyFinalFragment frag = - CreateKeyFinalFragment.newInstance( - mName, - mEmail, - mAdditionalEmails, - mPassphraseEdit.getText().toString() - ); + // save state + mCreateKeyActivity.mPassphrase = new Passphrase(mPassphraseEdit); + CreateKeyFinalFragment frag = CreateKeyFinalFragment.newInstance(); hideKeyboard(); - mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT); + mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java new file mode 100644 index 000000000..180a52a1c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.ui; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.Fragment; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; +import org.sufficientlysecure.keychain.ui.dialog.AddEmailDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; +import org.sufficientlysecure.keychain.ui.widget.EmailEditText; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; + +import java.util.ArrayList; +import java.util.List; + +public class CreateKeyStartFragment extends Fragment { + + CreateKeyActivity mCreateKeyActivity; + + View mCreateKey; + View mImportKey; + View mYubiKey; + TextView mCancel; + public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012; + + /** + * Creates new instance of this fragment + */ + public static CreateKeyStartFragment newInstance() { + CreateKeyStartFragment frag = new CreateKeyStartFragment(); + + Bundle args = new Bundle(); + + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.create_key_start_fragment, container, false); + + mCreateKey = view.findViewById(R.id.create_key_create_key_button); + mImportKey = view.findViewById(R.id.create_key_import_button); +// mYubiKey = view.findViewById(R.id.create_key_yubikey_button); + mCancel = (TextView) view.findViewById(R.id.create_key_cancel); + + if (mCreateKeyActivity.mFirstTime) { + mCancel.setText(R.string.first_time_skip); + } else { + mCancel.setText(R.string.btn_do_not_save); + } + + mCreateKey.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance(); + mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); + } + }); + + mImportKey.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(mCreateKeyActivity, ImportKeysActivity.class); + intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); + startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY); + } + }); + + mCancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finishSetup(null); + } + }); + + return view; + } + + + private void finishSetup(Intent srcData) { + if (mCreateKeyActivity.mFirstTime) { + Preferences prefs = Preferences.getPreferences(mCreateKeyActivity); + prefs.setFirstTime(false); + } + Intent intent = new Intent(mCreateKeyActivity, MainActivity.class); + // give intent through to display notify + if (srcData != null) { + intent.putExtras(srcData); + } + startActivity(intent); + mCreateKeyActivity.finish(); + } + + // workaround for https://code.google.com/p/android/issues/detail?id=61394 +// @Override +// public boolean onKeyDown(int keyCode, KeyEvent event) { +// return keyCode == KeyEvent.KEYCODE_MENU || super.onKeyDown(keyCode, event); +// } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) { + if (resultCode == Activity.RESULT_OK) { + finishSetup(data); + } + } else { + Log.e(Constants.TAG, "No valid request code!"); + } + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mCreateKeyActivity = (CreateKeyActivity) getActivity(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java index c808557a6..a92fb596c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java @@ -139,7 +139,7 @@ public class DecryptFilesFragment extends DecryptFragment { private void decryptAction() { if (mInputUri == null) { - Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR); + Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show(); return; } @@ -147,7 +147,9 @@ public class DecryptFilesFragment extends DecryptFragment { } private String removeEncryptedAppend(String name) { - if (name.endsWith(".asc") || name.endsWith(".gpg") || name.endsWith(".pgp")) { + if (name.endsWith(Constants.FILE_EXTENSION_ASC) + || name.endsWith(Constants.FILE_EXTENSION_PGP_MAIN) + || name.endsWith(Constants.FILE_EXTENSION_PGP_ALTERNATE)) { return name.substring(0, name.length() - 4); } return name; @@ -189,7 +191,7 @@ public class DecryptFilesFragment extends DecryptFragment { data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal()); data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri); - data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); + data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -263,7 +265,7 @@ public class DecryptFilesFragment extends DecryptFragment { data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal()); data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri); - data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); + data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -339,7 +341,7 @@ public class DecryptFilesFragment extends DecryptFragment { switch (requestCode) { case REQUEST_CODE_PASSPHRASE: { if (resultCode == Activity.RESULT_OK && data != null) { - mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); decryptOriginalFilename(); } return; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index 60103f344..63508e530 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; +import org.sufficientlysecure.keychain.util.Passphrase; public abstract class DecryptFragment extends Fragment { private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006; @@ -57,7 +58,7 @@ public abstract class DecryptFragment extends Fragment { // State - protected String mPassphrase; + protected Passphrase mPassphrase; protected byte[] mNfcDecryptedSessionKey; @Override @@ -100,7 +101,7 @@ public abstract class DecryptFragment extends Fragment { startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); } - protected void startNfcDecrypt(long subKeyId, String pin, byte[] encryptedSessionKey) { + protected void startNfcDecrypt(long subKeyId, Passphrase pin, byte[] encryptedSessionKey) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(getActivity(), NfcActivity.class); intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY); @@ -128,14 +129,14 @@ public abstract class DecryptFragment extends Fragment { mSignatureKeyId = signatureResult.getKeyId(); String userId = signatureResult.getPrimaryUserId(); - String[] userIdSplit = KeyRing.splitUserId(userId); - if (userIdSplit[0] != null) { - mSignatureName.setText(userIdSplit[0]); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); + if (userIdSplit.name != null) { + mSignatureName.setText(userIdSplit.name); } else { mSignatureName.setText(R.string.user_id_no_name); } - if (userIdSplit[1] != null) { - mSignatureEmail.setText(userIdSplit[1]); + if (userIdSplit.email != null) { + mSignatureEmail.setText(userIdSplit.email); } else { mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), mSignatureKeyId)); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java index 1e9e7bcb1..bc2ec014a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java @@ -161,7 +161,7 @@ public class DecryptTextActivity extends BaseActivity { if (sharedText != null) { loadFragment(savedInstanceState, sharedText); } else { - Notify.showNotify(this, R.string.error_invalid_data, Notify.Style.ERROR); + Notify.create(this, R.string.error_invalid_data, Notify.Style.ERROR).show(); } } else { Log.e(Constants.TAG, "ACTION_SEND received non-plaintext, this should not happen in this activity!"); @@ -175,7 +175,7 @@ public class DecryptTextActivity extends BaseActivity { if (extraText != null) { loadFragment(savedInstanceState, extraText); } else { - Notify.showNotify(this, R.string.error_invalid_data, Notify.Style.ERROR); + Notify.create(this, R.string.error_invalid_data, Notify.Style.ERROR).show(); } } else if (ACTION_DECRYPT_FROM_CLIPBOARD.equals(action)) { Log.d(Constants.TAG, "ACTION_DECRYPT_FROM_CLIPBOARD"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java index 1b34f6bf0..80a07214b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -132,7 +132,7 @@ public class DecryptTextFragment extends DecryptFragment { private void copyToClipboard(String text) { ClipboardReflection.copyToClipboard(getActivity(), text); - Notify.showNotify(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.INFO); + Notify.create(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.OK).show(); } @Override @@ -161,7 +161,7 @@ public class DecryptTextFragment extends DecryptFragment { // data data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal()); data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes()); - data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); + data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -247,7 +247,7 @@ public class DecryptTextFragment extends DecryptFragment { case REQUEST_CODE_PASSPHRASE: { if (resultCode == Activity.RESULT_OK && data != null) { - mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); decryptStart(); } else { getActivity().finish(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index de1eb64c2..86fb8dc80 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -67,6 +67,7 @@ import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; public class EditKeyFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks<Cursor> { @@ -100,7 +101,7 @@ public class EditKeyFragment extends LoaderFragment implements private SaveKeyringParcel mSaveKeyringParcel; private String mPrimaryUserId; - private String mCurrentPassphrase; + private Passphrase mCurrentPassphrase; /** * Creates new instance of this fragment @@ -267,7 +268,7 @@ public class EditKeyFragment extends LoaderFragment implements switch (requestCode) { case REQUEST_CODE_PASSPHRASE: { if (resultCode == Activity.RESULT_OK && data != null) { - mCurrentPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + mCurrentPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); // Prepare the loaders. Either re-connect with an existing ones, // or start new ones. getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditKeyFragment.this); @@ -386,7 +387,7 @@ public class EditKeyFragment extends LoaderFragment implements // cache new returned passphrase! mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel( - data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE), + (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE), null ); } @@ -545,7 +546,7 @@ public class EditKeyFragment extends LoaderFragment implements Messenger messenger = new Messenger(returnHandler); // pre-fill out primary name - String predefinedName = KeyRing.splitUserId(mPrimaryUserId)[0]; + String predefinedName = KeyRing.splitUserId(mPrimaryUserId).name; AddUserIdDialogFragment addUserIdDialog = AddUserIdDialogFragment.newInstance(messenger, predefinedName); @@ -576,11 +577,11 @@ public class EditKeyFragment extends LoaderFragment implements private void returnKeyringParcel() { if (mSaveKeyringParcel.mAddUserIds.size() == 0) { - Notify.showNotify(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR); + Notify.create(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR).show(); return; } if (mSaveKeyringParcel.mAddSubKeys.size() == 0) { - Notify.showNotify(getActivity(), R.string.edit_key_error_add_subkey, Notify.Style.ERROR); + Notify.create(getActivity(), R.string.edit_key_error_add_subkey, Notify.Style.ERROR).show(); return; } @@ -593,7 +594,7 @@ public class EditKeyFragment extends LoaderFragment implements getActivity().finish(); } - private void saveInDatabase(String passphrase) { + private void saveInDatabase(Passphrase passphrase) { Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString()); KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( @@ -640,7 +641,7 @@ public class EditKeyFragment extends LoaderFragment implements // fill values for this action Bundle data = new Bundle(); - data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 35dfcb87c..5438f667c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.Date; @@ -41,7 +42,7 @@ public abstract class EncryptActivity extends BaseActivity { public static final int REQUEST_CODE_NFC = 0x00008002; // For NFC data - protected String mSigningKeyPassphrase = null; + protected Passphrase mSigningKeyPassphrase = null; protected Date mNfcTimestamp = null; protected byte[] mNfcHash = null; @@ -64,7 +65,7 @@ public abstract class EncryptActivity extends BaseActivity { startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); } - protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) { + protected void startNfcSign(long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(this, NfcActivity.class); intent.setAction(NfcActivity.ACTION_SIGN_HASH); @@ -84,7 +85,7 @@ public abstract class EncryptActivity extends BaseActivity { switch (requestCode) { case REQUEST_CODE_PASSPHRASE: { if (resultCode == RESULT_OK && data != null) { - mSigningKeyPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + mSigningKeyPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); startEncrypt(); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java index baf445293..2a102c6c4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java @@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui; import android.net.Uri; +import org.sufficientlysecure.keychain.util.Passphrase; + import java.util.ArrayList; public interface EncryptActivityInterface { @@ -29,6 +31,8 @@ public interface EncryptActivityInterface { public boolean isUseArmor(); public boolean isUseCompression(); + public boolean isEncryptFilenames(); + public boolean isHiddenRecipients(); public long getSignatureKey(); public long[] getEncryptionKeys(); @@ -37,7 +41,7 @@ public interface EncryptActivityInterface { public void setEncryptionKeys(long[] encryptionKeys); public void setEncryptionUsers(String[] encryptionUsers); - public void setPassphrase(String passphrase); + public void setPassphrase(Passphrase passphrase); // ArrayList on purpose as only those are parcelable public ArrayList<Uri> getInputUris(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java index b862d5b11..fe9b05226 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ShareHelper; import java.util.ArrayList; @@ -62,14 +63,18 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi private static final int MODE_SYMMETRIC = 1; // model used by fragments - private long mEncryptionKeyIds[] = null; - private String mEncryptionUserIds[] = null; - private long mSigningKeyId = Constants.key.none; - private String mPassphrase = ""; private boolean mUseArmor = false; private boolean mUseCompression = true; private boolean mDeleteAfterEncrypt = false; private boolean mShareAfterEncrypt = false; + private boolean mEncryptFilenames = true; + private boolean mHiddenRecipients = false; + + private long mEncryptionKeyIds[] = null; + private String mEncryptionUserIds[] = null; + private long mSigningKeyId = Constants.key.none; + private Passphrase mPassphrase = new Passphrase(); + private ArrayList<Uri> mInputUris; private ArrayList<Uri> mOutputUris; private String mMessage = ""; @@ -89,6 +94,16 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi } @Override + public boolean isEncryptFilenames() { + return mEncryptFilenames; + } + + @Override + public boolean isHiddenRecipients() { + return mHiddenRecipients; + } + + @Override public long getSignatureKey() { return mSigningKeyId; } @@ -122,7 +137,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi } @Override - public void setPassphrase(String passphrase) { + public void setPassphrase(Passphrase passphrase) { mPassphrase = passphrase; } @@ -222,14 +237,15 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi } else { data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); } + data.setHiddenRecipients(mHiddenRecipients); data.setEnableAsciiArmorOutput(mUseArmor); data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); if (isModeSymmetric()) { Log.d(Constants.TAG, "Symmetric encryption enabled!"); - String passphrase = mPassphrase; - if (passphrase.length() == 0) { + Passphrase passphrase = mPassphrase; + if (passphrase.isEmpty()) { passphrase = null; } data.setSymmetricPassphrase(passphrase); @@ -268,14 +284,14 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE); sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris); } - sendIntent.setType("application/octet-stream"); + sendIntent.setType(Constants.ENCRYPTED_FILES_MIME); if (!isModeSymmetric() && mEncryptionUserIds != null) { Set<String> users = new HashSet<>(); for (String user : mEncryptionUserIds) { - String[] userId = KeyRing.splitUserId(user); - if (userId[1] != null) { - users.add(userId[1]); + KeyRing.UserId userId = KeyRing.splitUserId(user); + if (userId.email != null) { + users.add(userId.email); } } sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); @@ -287,7 +303,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi // file checks if (mInputUris.isEmpty()) { - Notify.showNotify(this, R.string.no_file_selected, Notify.Style.ERROR); + Notify.create(this, R.string.no_file_selected, Notify.Style.ERROR) + .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment)); return false; } else if (mInputUris.size() > 1 && !mShareAfterEncrypt) { // This should be impossible... @@ -301,11 +318,13 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi // symmetric encryption checks if (mPassphrase == null) { - Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR); + Notify.create(this, R.string.passphrases_do_not_match, Notify.Style.ERROR) + .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment)); return false; } if (mPassphrase.isEmpty()) { - Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR); + Notify.create(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) + .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment)); return false; } @@ -317,7 +336,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi // Files must be encrypted, only text can be signed-only right now if (!gotEncryptionKeys) { - Notify.showNotify(this, R.string.select_encryption_key, Notify.Style.ERROR); + Notify.create(this, R.string.select_encryption_key, Notify.Style.ERROR) + .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment)); return false; } } @@ -371,6 +391,16 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi notifyUpdate(); break; } + case R.id.check_encrypt_filenames: { + mEncryptFilenames = item.isChecked(); + notifyUpdate(); + break; + } +// case R.id.check_hidden_recipients: { +// mHiddenRecipients = item.isChecked(); +// notifyUpdate(); +// break; +// } default: { return super.onOptionsItemSelected(item); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 48737d223..4ba76d8ea 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -115,9 +115,9 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt } if (mEncryptInterface.getInputUris().contains(inputUri)) { - Notify.showNotify(getActivity(), + Notify.create(getActivity(), getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)), - Notify.Style.ERROR); + Notify.Style.ERROR).show(this); return; } @@ -137,36 +137,39 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt throw new IllegalStateException(); } Uri inputUri = mEncryptInterface.getInputUris().get(0); + String targetName = + (mEncryptInterface.isEncryptFilenames() ? "1" : FileHelper.getFilename(getActivity(), inputUri)) + + (mEncryptInterface.isUseArmor() ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { File file = new File(inputUri.getPath()); File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; - String targetName = FileHelper.getFilename(getActivity(), inputUri) + - (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"); File targetFile = new File(parentDir, targetName); FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file), getString(R.string.specify_file_to_encrypt_to), targetFile, REQUEST_CODE_OUTPUT); } else { - FileHelper.saveDocument(this, "*/*", FileHelper.getFilename(getActivity(), inputUri) + - (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"), REQUEST_CODE_OUTPUT); + FileHelper.saveDocument(this, "*/*", targetName, REQUEST_CODE_OUTPUT); } } private void encryptClicked(boolean share) { if (mEncryptInterface.getInputUris().isEmpty()) { - Notify.showNotify(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR); + Notify.create(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR).show(this); return; } if (share) { mEncryptInterface.getOutputUris().clear(); + int filenameCounter = 1; for (Uri uri : mEncryptInterface.getInputUris()) { - String targetName = FileHelper.getFilename(getActivity(), uri) + - (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"); + String targetName = + (mEncryptInterface.isEncryptFilenames() ? String.valueOf(filenameCounter) : FileHelper.getFilename(getActivity(), uri)) + + (mEncryptInterface.isUseArmor() ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); mEncryptInterface.getOutputUris().add(TemporaryStorageProvider.createFile(getActivity(), targetName)); + filenameCounter++; } mEncryptInterface.startEncrypt(true); } else { if (mEncryptInterface.getInputUris().size() > 1) { - Notify.showNotify(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR); + Notify.create(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR).show(this); return; } showOutputFileDialog(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java index 86731b162..36b3c08f9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java @@ -28,6 +28,7 @@ import android.view.ViewGroup; import android.widget.EditText; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Passphrase; public class EncryptSymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener { @@ -67,8 +68,13 @@ public class EncryptSymmetricFragment extends Fragment implements EncryptActivit @Override public void afterTextChanged(Editable s) { // update passphrase in EncryptActivity - if (mPassphrase.getText().toString().equals(mPassphraseAgain.getText().toString())) { - mEncryptInterface.setPassphrase(s.toString()); + Passphrase p1 = new Passphrase(mPassphrase.getText()); + Passphrase p2 = new Passphrase(mPassphraseAgain.getText()); + boolean passesEquals = (p1.equals(p2)); + p1.removeFromMemory(); + p2.removeFromMemory(); + if (passesEquals) { + mEncryptInterface.setPassphrase(new Passphrase(mPassphrase.getText())); } else { mEncryptInterface.setPassphrase(null); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index ee15cf7b5..c800153ae 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -36,7 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpConstants; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Preferences; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ShareHelper; import java.util.ArrayList; @@ -63,16 +63,19 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv private static final int MODE_SYMMETRIC = 1; // model used by fragments + private boolean mShareAfterEncrypt = false; + private boolean mUseCompression = true; + private boolean mHiddenRecipients = false; + private long mEncryptionKeyIds[] = null; private String mEncryptionUserIds[] = null; // TODO Constants.key.none? What's wrong with a null value? private long mSigningKeyId = Constants.key.none; - private String mPassphrase = ""; - private boolean mShareAfterEncrypt = false; + private Passphrase mPassphrase = new Passphrase(); + private ArrayList<Uri> mInputUris; private ArrayList<Uri> mOutputUris; private String mMessage = ""; - private boolean mUseCompression = true; public boolean isModeSymmetric() { return MODE_SYMMETRIC == mCurrentMode; @@ -84,11 +87,21 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv } @Override + public boolean isEncryptFilenames() { + return false; + } + + @Override public boolean isUseCompression() { return mUseCompression; } @Override + public boolean isHiddenRecipients() { + return mHiddenRecipients; + } + + @Override public long getSignatureKey() { return mSigningKeyId; } @@ -122,7 +135,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv } @Override - public void setPassphrase(String passphrase) { + public void setPassphrase(Passphrase passphrase) { + mPassphrase.removeFromMemory(); mPassphrase = passphrase; } @@ -184,8 +198,9 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv // Copy to clipboard copyToClipboard(result.getResultBytes()); result.createNotify(EncryptTextActivity.this).show(); - // Notify.showNotify(EncryptTextActivity.this, - // R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO); + // Notify.create(EncryptTextActivity.this, + // R.string.encrypt_sign_clipboard_successful, Notify.Style.OK) + // .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); } } @@ -202,6 +217,7 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv } else { data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); } + data.setHiddenRecipients(mHiddenRecipients); data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); @@ -210,8 +226,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv if (isModeSymmetric()) { Log.d(Constants.TAG, "Symmetric encryption enabled!"); - String passphrase = mPassphrase; - if (passphrase.length() == 0) { + Passphrase passphrase = mPassphrase; + if (passphrase.isEmpty()) { passphrase = null; } data.setSymmetricPassphrase(passphrase); @@ -247,15 +263,15 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv private Intent createSendIntent(byte[] resultBytes) { Intent sendIntent; sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.setType("text/plain"); + sendIntent.setType(Constants.ENCRYPTED_TEXT_MIME); sendIntent.putExtra(Intent.EXTRA_TEXT, new String(resultBytes)); if (!isModeSymmetric() && mEncryptionUserIds != null) { Set<String> users = new HashSet<>(); for (String user : mEncryptionUserIds) { - String[] userId = KeyRing.splitUserId(user); - if (userId[1] != null) { - users.add(userId[1]); + KeyRing.UserId userId = KeyRing.splitUserId(user); + if (userId.email != null) { + users.add(userId.email); } } // pass trough email addresses as extra for email applications @@ -266,7 +282,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv protected boolean inputIsValid() { if (mMessage == null) { - Notify.showNotify(this, R.string.error_message, Notify.Style.ERROR); + Notify.create(this, R.string.error_message, Notify.Style.ERROR) + .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); return false; } @@ -274,11 +291,13 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv // symmetric encryption checks if (mPassphrase == null) { - Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR); + Notify.create(this, R.string.passphrases_do_not_match, Notify.Style.ERROR) + .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); return false; } if (mPassphrase.isEmpty()) { - Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR); + Notify.create(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) + .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); return false; } @@ -289,7 +308,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv && mEncryptionKeyIds.length > 0); if (!gotEncryptionKeys && mSigningKeyId == 0) { - Notify.showNotify(this, R.string.select_encryption_or_signature_key, Notify.Style.ERROR); + Notify.create(this, R.string.select_encryption_or_signature_key, Notify.Style.ERROR) + .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment)); return false; } } @@ -353,6 +373,11 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv notifyUpdate(); break; } +// case R.id.check_hidden_recipients: { +// mHiddenRecipients = item.isChecked(); +// notifyUpdate(); +// break; +// } default: { return super.onOptionsItemSelected(item); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java deleted file mode 100644 index 393e15cfa..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package org.sufficientlysecure.keychain.ui; - -import android.content.Intent; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import android.view.Window; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Preferences; - -public class FirstTimeActivity extends BaseActivity { - - View mCreateKey; - View mImportKey; - View mSkipSetup; - - public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012; - - @Override - protected void onCreate(Bundle savedInstanceState) { - supportRequestWindowFeature(Window.FEATURE_NO_TITLE); - - super.onCreate(savedInstanceState); - - mCreateKey = findViewById(R.id.first_time_create_key); - mImportKey = findViewById(R.id.first_time_import_key); - mSkipSetup = findViewById(R.id.first_time_cancel); - - mSkipSetup.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finishSetup(null); - } - }); - - mImportKey.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(FirstTimeActivity.this, ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); - startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY); - } - }); - - mCreateKey.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(FirstTimeActivity.this, CreateKeyActivity.class); - startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY); - } - }); - } - - @Override - protected void initLayout() { - setContentView(R.layout.first_time_activity); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) { - if (resultCode == RESULT_OK) { - finishSetup(data); - } - } else { - Log.e(Constants.TAG, "No valid request code!"); - } - } - - private void finishSetup(Intent srcData) { - Preferences prefs = Preferences.getPreferences(this); - prefs.setFirstTime(false); - Intent intent = new Intent(this, MainActivity.class); - // give intent through to display notify - if (srcData != null) { - intent.putExtras(srcData); - } - startActivity(intent); - finish(); - } - - // workaround for https://code.google.com/p/android/issues/detail?id=61394 - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return keyCode == KeyEvent.KEYCODE_MENU || super.onKeyDown(keyCode, event); - } - -} 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 d51e2c7fc..9143609ad 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -26,6 +26,7 @@ import android.os.Messenger; import android.support.v4.app.Fragment; import android.view.View; import android.view.View.OnClickListener; +import android.view.ViewGroup; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -277,7 +278,8 @@ public class ImportKeysActivity extends BaseActivity { private boolean isFingerprintValid(String fingerprint) { if (fingerprint == null || fingerprint.length() < 40) { - Notify.showNotify(this, R.string.import_qr_code_too_short_fingerprint, Notify.Style.ERROR); + Notify.create(this, R.string.import_qr_code_too_short_fingerprint, Notify.Style.ERROR) + .show((ViewGroup) findViewById(R.id.import_snackbar)); return false; } else { return true; @@ -329,7 +331,8 @@ public class ImportKeysActivity extends BaseActivity { return; } - result.createNotify(ImportKeysActivity.this).show(); + result.createNotify(ImportKeysActivity.this) + .show((ViewGroup) findViewById(R.id.import_snackbar)); } } }; @@ -372,7 +375,8 @@ public class ImportKeysActivity extends BaseActivity { startService(intent); } catch (IOException e) { Log.e(Constants.TAG, "Problem writing cache file", e); - Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR); + Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR) + .show((ViewGroup) findViewById(R.id.import_snackbar)); } } else if (ls instanceof ImportKeysListFragment.CloudLoaderState) { ImportKeysListFragment.CloudLoaderState sls = (ImportKeysListFragment.CloudLoaderState) ls; @@ -412,7 +416,8 @@ public class ImportKeysActivity extends BaseActivity { // start service with intent startService(intent); } else { - Notify.showNotify(this, R.string.error_nothing_import, Notify.Style.ERROR); + Notify.create(this, R.string.error_nothing_import, Notify.Style.ERROR) + .show((ViewGroup) findViewById(R.id.import_snackbar)); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index cc8b47971..1c1e5fe99 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -154,7 +154,7 @@ public class ImportKeysProxyActivity extends FragmentActivity { String fingerprint = null; // example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282 - if (uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { + if (uri != null && uri.getScheme() != null && uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 8c34efba2..5f1189deb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -169,6 +169,22 @@ public class KeyListFragment extends LoaderFragment mStickyList.setDrawingListUnderStickyHeader(false); mStickyList.setFastScrollEnabled(true); + // Adds an empty footer view so that the Floating Action Button won't block content + // in last few rows. + View footer = new View(getActivity()); + + int spacing = (int) android.util.TypedValue.applyDimension( + android.util.TypedValue.COMPLEX_UNIT_DIP, 72, getResources().getDisplayMetrics() + ); + + android.widget.AbsListView.LayoutParams params = new android.widget.AbsListView.LayoutParams( + android.widget.AbsListView.LayoutParams.MATCH_PARENT, + spacing + ); + + footer.setLayoutParams(params); + mStickyList.addFooterView(footer, null, false); + /* * Multi-selection */ @@ -369,13 +385,13 @@ public class KeyListFragment extends LoaderFragment /** * Show dialog to delete key * - * @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not + * @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not */ public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) { // Can only work on singular secret keys if (hasSecret && masterKeyIds.length > 1) { - Notify.showNotify(getActivity(), R.string.secret_cannot_multiple, - Notify.Style.ERROR); + Notify.create(getActivity(), R.string.secret_cannot_multiple, + Notify.Style.ERROR).show(); return; } @@ -468,28 +484,29 @@ public class KeyListFragment extends LoaderFragment case R.id.menu_key_list_debug_read: try { KeychainDatabase.debugBackup(getActivity(), true); - Notify.showNotify(getActivity(), "Restored debug_backup.db", Notify.Style.INFO); + Notify.create(getActivity(), "Restored debug_backup.db", Notify.Style.OK).show(); getActivity().getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null); } catch (IOException e) { Log.e(Constants.TAG, "IO Error", e); - Notify.showNotify(getActivity(), "IO Error " + e.getMessage(), Notify.Style.ERROR); + Notify.create(getActivity(), "IO Error " + e.getMessage(), Notify.Style.ERROR).show(); } return true; case R.id.menu_key_list_debug_write: try { KeychainDatabase.debugBackup(getActivity(), false); - Notify.showNotify(getActivity(), "Backup to debug_backup.db completed", Notify.Style.INFO); + Notify.create(getActivity(), "Backup to debug_backup.db completed", Notify.Style.OK).show(); } catch (IOException e) { Log.e(Constants.TAG, "IO Error", e); - Notify.showNotify(getActivity(), "IO Error: " + e.getMessage(), Notify.Style.ERROR); + Notify.create(getActivity(), "IO Error: " + e.getMessage(), Notify.Style.ERROR).show(); } return true; case R.id.menu_key_list_debug_first_time: Preferences prefs = Preferences.getPreferences(getActivity()); prefs.setFirstTime(true); - Intent intent = new Intent(getActivity(), FirstTimeActivity.class); + Intent intent = new Intent(getActivity(), CreateKeyActivity.class); + intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true); startActivity(intent); getActivity().finish(); return true; @@ -688,14 +705,14 @@ public class KeyListFragment extends LoaderFragment { // set name and stuff, common to both key types String userId = cursor.getString(INDEX_USER_ID); - String[] userIdSplit = KeyRing.splitUserId(userId); - if (userIdSplit[0] != null) { - h.mMainUserId.setText(highlighter.highlight(userIdSplit[0])); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); + if (userIdSplit.name != null) { + h.mMainUserId.setText(highlighter.highlight(userIdSplit.name)); } else { h.mMainUserId.setText(R.string.user_id_no_name); } - if (userIdSplit[1] != null) { - h.mMainUserIdRest.setText(highlighter.highlight(userIdSplit[1])); + if (userIdSplit.email != null) { + h.mMainUserIdRest.setText(highlighter.highlight(userIdSplit.email)); h.mMainUserIdRest.setVisibility(View.VISIBLE); } else { h.mMainUserIdRest.setVisibility(View.GONE); @@ -908,5 +925,4 @@ public class KeyListFragment extends LoaderFragment } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index b6b2fcb8a..5fa3edba4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -54,7 +54,9 @@ public class MainActivity extends MaterialNavigationDrawer implements FabContain // if this is the first time show first time activity Preferences prefs = Preferences.getPreferences(this); if (prefs.isFirstTime()) { - startActivity(new Intent(this, FirstTimeActivity.class)); + Intent intent = new Intent(this, CreateKeyActivity.class); + intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true); + startActivity(intent); finish(); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index b06cf0abd..b77637696 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; /** @@ -212,9 +213,9 @@ public class PassphraseDialogActivity extends FragmentActivity { // the catch clause doesn't return. try { String mainUserId = mSecretRing.getPrimaryUserIdWithFallback(); - String[] mainUserIdSplit = KeyRing.splitUserId(mainUserId); - if (mainUserIdSplit[0] != null) { - userId = mainUserIdSplit[0]; + KeyRing.UserId mainUserIdSplit = KeyRing.splitUserId(mainUserId); + if (mainUserIdSplit.name != null) { + userId = mainUserIdSplit.name; } else { userId = getString(R.string.user_id_no_name); } @@ -240,7 +241,7 @@ public class PassphraseDialogActivity extends FragmentActivity { break; // special case: empty passphrase just returns the empty passphrase case PASSPHRASE_EMPTY: - finishCaching(""); + finishCaching(new Passphrase("")); default: message = "This should not happen!"; break; @@ -322,7 +323,7 @@ public class PassphraseDialogActivity extends FragmentActivity { positive.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - final String passphrase = mPassphraseEditText.getText().toString(); + final Passphrase passphrase = new Passphrase(mPassphraseEditText); // Early breakout if we are dealing with a symmetric key if (mSecretRing == null) { @@ -399,7 +400,7 @@ public class PassphraseDialogActivity extends FragmentActivity { }); } - private void finishCaching(String passphrase) { + private void finishCaching(Passphrase passphrase) { // any indication this isn't needed anymore, don't do it. if (mIsCancelled || getActivity() == null) { return; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java index d3c1d971a..43af07bbe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java @@ -80,7 +80,7 @@ public class QrCodeViewActivity extends BaseActivity { KeychainContract.KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB); if (blob == null) { Log.e(Constants.TAG, "key not found!"); - Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR); + Notify.create(this, R.string.error_key_not_found, Style.ERROR).show(); ActivityCompat.finishAfterTransition(QrCodeViewActivity.this); } @@ -102,7 +102,7 @@ public class QrCodeViewActivity extends BaseActivity { }); } catch (ProviderHelper.NotFoundException e) { Log.e(Constants.TAG, "key not found!", e); - Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR); + Notify.create(this, R.string.error_key_not_found, Style.ERROR).show(); ActivityCompat.finishAfterTransition(QrCodeViewActivity.this); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java index d0cea5f05..863aef65f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java @@ -205,7 +205,7 @@ public class SafeSlingerActivity extends BaseActivity { activity.startService(intent); } catch (IOException e) { Log.e(Constants.TAG, "Problem writing cache file", e); - Notify.showNotify(activity, "Problem writing cache file!", Notify.Style.ERROR); + Notify.create(activity, "Problem writing cache file!", Notify.Style.ERROR).show(); } } else { // give everything else down to KeyListActivity! 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 70c590728..435a455dc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -21,18 +21,12 @@ package org.sufficientlysecure.keychain.ui; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityOptions; import android.content.Intent; -import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; -import android.nfc.NdefMessage; -import android.nfc.NdefRecord; -import android.nfc.NfcAdapter; -import android.nfc.NfcEvent; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; @@ -40,7 +34,6 @@ import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.provider.ContactsContract; -import android.provider.Settings; import android.support.v4.app.ActivityCompat; import android.support.v4.app.FragmentManager; import android.support.v4.app.LoaderManager; @@ -59,9 +52,7 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; - import com.getbase.floatingactionbutton.FloatingActionButton; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; @@ -76,6 +67,8 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; @@ -84,6 +77,7 @@ import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.ExportHelper; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.NfcHelper; import org.sufficientlysecure.keychain.util.Preferences; import java.util.ArrayList; @@ -93,8 +87,8 @@ public class ViewKeyActivity extends BaseActivity implements LoaderManager.LoaderCallbacks<Cursor> { static final int REQUEST_QR_FINGERPRINT = 1; - static final int REQUEST_DELETE= 2; - static final int REQUEST_EXPORT= 3; + static final int REQUEST_DELETE = 2; + static final int REQUEST_EXPORT = 3; ExportHelper mExportHelper; ProviderHelper mProviderHelper; @@ -115,11 +109,7 @@ public class ViewKeyActivity extends BaseActivity implements private CardView mQrCodeLayout; // NFC - private NfcAdapter mNfcAdapter; - private NfcAdapter.CreateNdefMessageCallback mNdefCallback; - private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback; - private byte[] mNfcKeyringBytes; - private static final int NFC_SENT = 1; + private NfcHelper mNfcHelper; private static final int LOADER_ID_UNIFIED = 0; @@ -256,7 +246,7 @@ public class ViewKeyActivity extends BaseActivity implements mActionNfc.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - invokeNfcBeam(); + mNfcHelper.invokeNfcBeam(); } }); @@ -264,7 +254,8 @@ public class ViewKeyActivity extends BaseActivity implements // or start new ones. getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); - initNfc(mDataUri); + mNfcHelper = new NfcHelper(this, mProviderHelper); + mNfcHelper.initNfc(mDataUri); } @@ -291,31 +282,31 @@ public class ViewKeyActivity extends BaseActivity implements return true; } case R.id.menu_key_view_export_file: { - Intent mIntent = new Intent(this,PassphraseDialogActivity.class); - long keyId=0; try { - keyId = new ProviderHelper(this) - .getCachedPublicKeyRing(mDataUri) - .extractOrGetMasterKeyId(); - } catch (PgpKeyNotFoundException e) { - e.printStackTrace(); + if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) { + exportToFile(mDataUri, mExportHelper, mProviderHelper); + return true; + } + + startPassphraseActivity(REQUEST_EXPORT); + } catch (PassphraseCacheService.KeyNotFoundException e) { + // This happens when the master key is stripped + exportToFile(mDataUri, mExportHelper, mProviderHelper); } - mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId); - startActivityForResult(mIntent,REQUEST_EXPORT); return true; } case R.id.menu_key_view_delete: { - Intent mIntent = new Intent(this,PassphraseDialogActivity.class); - long keyId=0; try { - keyId = new ProviderHelper(this) - .getCachedPublicKeyRing(mDataUri) - .extractOrGetMasterKeyId(); - } catch (PgpKeyNotFoundException e) { - e.printStackTrace(); + if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) { + deleteKey(); + return true; + } + + startPassphraseActivity(REQUEST_DELETE); + } catch (PassphraseCacheService.KeyNotFoundException e) { + // This happens when the master key is stripped + deleteKey(); } - mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId); - startActivityForResult(mIntent,REQUEST_DELETE); return true; } case R.id.menu_key_view_advanced: { @@ -328,7 +319,7 @@ public class ViewKeyActivity extends BaseActivity implements try { updateFromKeyserver(mDataUri, mProviderHelper); } catch (ProviderHelper.NotFoundException e) { - Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR); + Notify.create(this, R.string.error_key_not_found, Notify.Style.ERROR).show(); } return true; } @@ -364,41 +355,6 @@ public class ViewKeyActivity extends BaseActivity implements return true; } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void invokeNfcBeam() { - // Check if device supports NFC - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { - Notify.createNotify(this, R.string.no_nfc_support, Notify.LENGTH_LONG, Notify.Style.ERROR).show(); - return; - } - // Check for available NFC Adapter - mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { - Notify.createNotify(this, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { - @Override - public void onAction() { - Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS); - startActivity(intentSettings); - } - }, R.string.menu_nfc_preferences).show(); - - return; - } - - if (!mNfcAdapter.isNdefPushEnabled()) { - Notify.createNotify(this, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { - @Override - public void onAction() { - Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS); - startActivity(intentSettings); - } - }, R.string.menu_beam_preferences).show(); - - return; - } - - mNfcAdapter.invokeBeam(this); - } private void scanQrCode() { Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class); @@ -415,7 +371,7 @@ public class ViewKeyActivity extends BaseActivity implements private void certifyImmediate() { Intent intent = new Intent(this, CertifyKeyActivity.class); - intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId}); + intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] {mMasterKeyId}); startCertifyIntent(intent); } @@ -464,22 +420,32 @@ public class ViewKeyActivity extends BaseActivity implements ActivityCompat.startActivity(this, qrCodeIntent, opts); } - private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) - throws ProviderHelper.NotFoundException { - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri); - - HashMap<String, Object> data = providerHelper.getGenericData( - baseUri, - new String[]{KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET}, - new int[]{ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER}); + private void startPassphraseActivity(int requestCode) { + Intent intent = new Intent(this, PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mMasterKeyId); + startActivityForResult(intent, requestCode); + } - exportHelper.showExportKeysDialog( - new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)}, - Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0) - ); + private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) { + try { + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri); + + HashMap<String, Object> data = providerHelper.getGenericData( + baseUri, + new String[] {KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET}, + new int[] {ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER}); + + exportHelper.showExportKeysDialog( + new long[] {(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)}, + Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0) + ); + } catch (ProviderHelper.NotFoundException e) { + Notify.create(this, R.string.error_key_not_found, Notify.Style.ERROR).show(); + Log.e(Constants.TAG, "Key not found", e); + } } - private void deleteKey(Uri dataUri, ExportHelper exportHelper) { + private void deleteKey() { // Message is received after key is deleted Handler returnHandler = new Handler() { @Override @@ -491,7 +457,11 @@ public class ViewKeyActivity extends BaseActivity implements } }; - exportHelper.deleteKey(dataUri, returnHandler); + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, + new long[] {mMasterKeyId}); + deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog"); } @Override @@ -507,32 +477,27 @@ public class ViewKeyActivity extends BaseActivity implements String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT); if (fp == null) { - Notify.createNotify(this, "Error scanning fingerprint!", + Notify.create(this, "Error scanning fingerprint!", Notify.LENGTH_LONG, Notify.Style.ERROR).show(); return; } if (mFingerprint.equalsIgnoreCase(fp)) { certifyImmediate(); } else { - Notify.createNotify(this, "Fingerprints did not match!", + Notify.create(this, "Fingerprints did not match!", Notify.LENGTH_LONG, Notify.Style.ERROR).show(); } return; } - if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK){ - deleteKey(mDataUri, mExportHelper); - } - if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK){ - try { - exportToFile(mDataUri, mExportHelper, mProviderHelper); - } catch (ProviderHelper.NotFoundException e) { - Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR); - Log.e(Constants.TAG, "Key not found", e); - } + if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK) { + deleteKey(); } + if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK) { + exportToFile(mDataUri, mExportHelper, mProviderHelper); + } if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); @@ -545,14 +510,14 @@ public class ViewKeyActivity extends BaseActivity implements private void encrypt(Uri dataUri, boolean text) { // If there is no encryption key, don't bother. if (!mHasEncrypt) { - Notify.showNotify(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR); + Notify.create(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR).show(); return; } try { long keyId = new ProviderHelper(this) .getCachedPublicKeyRing(dataUri) .extractOrGetMasterKeyId(); - long[] encryptionKeyIds = new long[]{keyId}; + long[] encryptionKeyIds = new long[] {keyId}; Intent intent; if (text) { intent = new Intent(this, EncryptTextActivity.class); @@ -690,98 +655,9 @@ public class ViewKeyActivity extends BaseActivity implements loadTask.execute(); } - /** - * NFC: Initialize NFC sharing if OS and device supports it - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - private void initNfc(final Uri dataUri) { - // check if NFC Beam is supported (>= Android 4.1) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - - // Implementation for the CreateNdefMessageCallback interface - mNdefCallback = new NfcAdapter.CreateNdefMessageCallback() { - @Override - public NdefMessage createNdefMessage(NfcEvent event) { - /* - * When a device receives a push with an AAR in it, the application specified in the AAR is - * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to - * guarantee that this activity starts when receiving a beamed message. For now, this code - * uses the tag dispatch system. - */ - return new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME, - mNfcKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME)); - } - }; - - // Implementation for the OnNdefPushCompleteCallback interface - mNdefCompleteCallback = new NfcAdapter.OnNdefPushCompleteCallback() { - @Override - public void onNdefPushComplete(NfcEvent event) { - // A handler is needed to send messages to the activity when this - // callback occurs, because it happens from a binder thread - mNfcHandler.obtainMessage(NFC_SENT).sendToTarget(); - } - }; - - // Check for available NFC Adapter - mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - if (mNfcAdapter != null) { - /* - * Retrieve mNfcKeyringBytes here asynchronously (to not block the UI) - * and init nfc adapter afterwards. - * mNfcKeyringBytes can not be retrieved in createNdefMessage, because this process - * has no permissions to query the Uri. - */ - AsyncTask<Void, Void, Void> initTask = - new AsyncTask<Void, Void, Void>() { - protected Void doInBackground(Void... unused) { - try { - Uri blobUri = - KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri); - mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData( - blobUri, - KeychainContract.KeyRingData.KEY_RING_DATA, - ProviderHelper.FIELD_TYPE_BLOB); - } catch (ProviderHelper.NotFoundException e) { - Log.e(Constants.TAG, "key not found!", e); - } - - // no AsyncTask return (Void) - return null; - } - - protected void onPostExecute(Void unused) { - // Register callback to set NDEF message - mNfcAdapter.setNdefPushMessageCallback(mNdefCallback, - ViewKeyActivity.this); - // Register callback to listen for message-sent success - mNfcAdapter.setOnNdefPushCompleteCallback(mNdefCompleteCallback, - ViewKeyActivity.this); - } - }; - - initTask.execute(); - } - } - } - - /** - * NFC: This handler receives a message from onNdefPushComplete - */ - private final Handler mNfcHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case NFC_SENT: - Notify.showNotify( - ViewKeyActivity.this, R.string.nfc_successful, Notify.Style.INFO); - break; - } - } - }; // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{ + static final String[] PROJECTION = new String[] { KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID, KeychainContract.KeyRings.USER_ID, @@ -849,9 +725,9 @@ public class ViewKeyActivity extends BaseActivity implements startFragment(mIsSecret, fpData); // get name, email, and comment from USER_ID - String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); - if (mainUserId[0] != null) { - mName.setText(mainUserId[0]); + KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); + if (mainUserId.name != null) { + mName.setText(mainUserId.name); } else { mName.setText(R.string.user_id_no_name); } @@ -892,6 +768,7 @@ public class ViewKeyActivity extends BaseActivity implements } else if (mIsExpired) { if (mIsSecret) { mStatusText.setText(R.string.view_key_expired_secret); + mName.setText(mainUserId.name); } else { mStatusText.setText(R.string.view_key_expired); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java index 0654f0c9a..f17d6e0fd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java @@ -197,9 +197,9 @@ public class ViewKeyAdvActivity extends BaseActivity implements case LOADER_ID_UNIFIED: { if (data.moveToFirst()) { // get name, email, and comment from USER_ID - String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); - if (mainUserId[0] != null) { - setTitle(mainUserId[0]); + KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); + if (mainUserId.name != null) { + setTitle(mainUserId.name); } else { setTitle(R.string.user_id_no_name); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvCertsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvCertsFragment.java index 90d7a400f..f5c8a87b1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvCertsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvCertsFragment.java @@ -237,9 +237,9 @@ public class ViewKeyAdvCertsFragment extends LoaderFragment implements TextView wSignStatus = (TextView) view.findViewById(R.id.signStatus); String signerKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), cursor.getLong(mIndexSignerKeyId)); - String[] userId = KeyRing.splitUserId(cursor.getString(mIndexSignerUserId)); - if (userId[0] != null) { - wSignerName.setText(userId[0]); + KeyRing.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexSignerUserId)); + if (userId.name != null) { + wSignerName.setText(userId.name); } else { wSignerName.setText(R.string.user_id_no_name); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java index 95a6faea9..6bd3a9303 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java @@ -52,6 +52,7 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.NfcHelper; import java.io.IOException; @@ -68,10 +69,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements private View mFingerprintClipboardButton; private View mKeyShareButton; private View mKeyClipboardButton; + private View mKeyNfcButton; private ImageButton mKeySafeSlingerButton; private View mKeyUploadButton; ProviderHelper mProviderHelper; + NfcHelper mNfcHelper; private static final int LOADER_ID_UNIFIED = 0; @@ -83,6 +86,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements View view = inflater.inflate(R.layout.view_key_adv_share_fragment, getContainer()); mProviderHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity()); + mNfcHelper = new NfcHelper(getActivity(), mProviderHelper); mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint); mQrCode = (ImageView) view.findViewById(R.id.view_key_qr_code); @@ -90,6 +94,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements mFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share); mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard); mKeyShareButton = view.findViewById(R.id.view_key_action_key_share); + mKeyNfcButton = view.findViewById(R.id.view_key_action_key_nfc); mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard); mKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger); mKeyUploadButton = view.findViewById(R.id.view_key_action_upload); @@ -128,6 +133,14 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements share(mDataUri, mProviderHelper, false, true); } }); + + mKeyNfcButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mNfcHelper.invokeNfcBeam(); + } + }); + mKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -186,13 +199,13 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements } else { message = getResources().getString(R.string.key_copied_to_clipboard); } - Notify.showNotify(getActivity(), message, Notify.Style.OK); + Notify.create(getActivity(), message, Notify.Style.OK).show(); } else { // Android will fail with android.os.TransactionTooLargeException if key is too big // see http://www.lonestarprod.com/?p=34 if (content.length() >= 86389) { - Notify.showNotify(getActivity(), R.string.key_too_big_for_sharing, - Notify.Style.ERROR); + Notify.create(getActivity(), R.string.key_too_big_for_sharing, + Notify.Style.ERROR).show(); return; } @@ -210,10 +223,10 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements } } catch (PgpGeneralException | IOException e) { Log.e(Constants.TAG, "error processing key!", e); - Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR); + Notify.create(getActivity(), R.string.error_key_processing, Notify.Style.ERROR).show(); } catch (ProviderHelper.NotFoundException e) { Log.e(Constants.TAG, "key not found!", e); - Notify.showNotify(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR); + Notify.create(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR).show(); } } @@ -255,9 +268,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements // Prepare the loaders. Either re-connect with an existing ones, // or start new ones. getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); + + // Prepare the NfcHelper + mNfcHelper.initNfc(mDataUri); } - static final String[] UNIFIED_PROJECTION = new String[]{ + static final String[] UNIFIED_PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET, KeyRings.USER_ID, KeyRings.FINGERPRINT, KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.IS_EXPIRED, @@ -362,4 +378,5 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements startActivityForResult(uploadIntent, 0); } -} + +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 429feb075..db88de676 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -140,25 +140,25 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { // main user id String userId = entry.getUserIds().get(0); - String[] userIdSplit = KeyRing.splitUserId(userId); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); // name - if (userIdSplit[0] != null) { + if (userIdSplit.name != null) { // show red user id if it is a secret key if (entry.isSecretKey()) { holder.mainUserId.setText(mActivity.getString(R.string.secret_key) - + " " + userIdSplit[0]); + + " " + userIdSplit.name); } else { - holder.mainUserId.setText(highlighter.highlight(userIdSplit[0])); + holder.mainUserId.setText(highlighter.highlight(userIdSplit.name)); } } else { holder.mainUserId.setText(R.string.user_id_no_name); } // email - if (userIdSplit[1] != null) { + if (userIdSplit.email != null) { holder.mainUserIdRest.setVisibility(View.VISIBLE); - holder.mainUserIdRest.setText(highlighter.highlight(userIdSplit[1])); + holder.mainUserIdRest.setText(highlighter.highlight(userIdSplit.email)); } else { holder.mainUserIdRest.setVisibility(View.GONE); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java index 70b57aa93..5218273a0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java @@ -33,7 +33,6 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import java.util.ArrayList; @@ -83,9 +82,9 @@ public class MultiUserIdsAdapter extends CursorAdapter { { // first one String userId = uids.get(0); - String[] splitUserId = KeyRing.splitUserId(userId); - if (splitUserId[0] != null) { - vName.setText(splitUserId[0]); + KeyRing.UserId splitUserId = KeyRing.splitUserId(userId); + if (splitUserId.name != null) { + vName.setText(splitUserId.name); } else { vName.setText(R.string.user_id_no_name); } @@ -93,9 +92,9 @@ public class MultiUserIdsAdapter extends CursorAdapter { if (isHeader == 1) { vHeaderId.setVisibility(View.VISIBLE); String message; - if (splitUserId[0] != null) { + if (splitUserId.name != null) { message = mContext.getString(R.string.section_uids_to_certify) + - splitUserId[0]; + splitUserId.name; } else { message = mContext.getString(R.string.section_uids_to_certify) + context.getString(R.string.user_id_no_name); @@ -108,13 +107,13 @@ public class MultiUserIdsAdapter extends CursorAdapter { StringBuilder lines = new StringBuilder(); for (String uid : uids) { - String[] splitUserId = KeyRing.splitUserId(uid); - if (splitUserId[1] == null) { + KeyRing.UserId splitUserId = KeyRing.splitUserId(uid); + if (splitUserId.email == null) { continue; } - lines.append(splitUserId[1]); - if (splitUserId[2] != null) { - lines.append(" (").append(splitUserId[2]).append(")"); + lines.append(splitUserId.email); + if (splitUserId.comment != null) { + lines.append(" (").append(splitUserId.comment).append(")"); } lines.append('\n'); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java index 892e30a54..3308a4500 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java @@ -122,16 +122,16 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter { ViewHolderItem h = (ViewHolderItem) view.getTag(); String userId = cursor.getString(mIndexUserId); - String[] userIdSplit = KeyRing.splitUserId(userId); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); - if (userIdSplit[0] != null) { - h.mainUserId.setText(highlighter.highlight(userIdSplit[0])); + if (userIdSplit.name != null) { + h.mainUserId.setText(highlighter.highlight(userIdSplit.name)); } else { h.mainUserId.setText(R.string.user_id_no_name); } - if (userIdSplit[1] != null) { + if (userIdSplit.email != null) { h.mainUserIdRest.setVisibility(View.VISIBLE); - h.mainUserIdRest.setText(highlighter.highlight(userIdSplit[1])); + h.mainUserIdRest.setText(highlighter.highlight(userIdSplit.email)); } else { h.mainUserIdRest.setVisibility(View.GONE); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java index 1cf3f4d38..c68c078ad 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java @@ -72,20 +72,20 @@ public class UserIdsAdapter extends UserAttributesAdapter { vDeleteButton.setVisibility(View.GONE); // not used String userId = cursor.getString(INDEX_USER_ID); - String[] splitUserId = KeyRing.splitUserId(userId); - if (splitUserId[0] != null) { - vName.setText(splitUserId[0]); + KeyRing.UserId splitUserId = KeyRing.splitUserId(userId); + if (splitUserId.name != null) { + vName.setText(splitUserId.name); } else { vName.setText(R.string.user_id_no_name); } - if (splitUserId[1] != null) { - vAddress.setText(splitUserId[1]); + if (splitUserId.email != null) { + vAddress.setText(splitUserId.email); vAddress.setVisibility(View.VISIBLE); } else { vAddress.setVisibility(View.GONE); } - if (splitUserId[2] != null) { - vComment.setText(splitUserId[2]); + if (splitUserId.comment != null) { + vComment.setText(splitUserId.comment); vComment.setVisibility(View.VISIBLE); } else { vComment.setVisibility(View.GONE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java index 970855c77..c7197b46d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAddedAdapter.java @@ -92,20 +92,20 @@ public class UserIdsAddedAdapter extends ArrayAdapter<String> { // save reference to model item holder.mModel = getItem(position); - String[] splitUserId = KeyRing.splitUserId(holder.mModel); - if (splitUserId[0] != null) { - holder.vName.setText(splitUserId[0]); + KeyRing.UserId splitUserId = KeyRing.splitUserId(holder.mModel); + if (splitUserId.name != null) { + holder.vName.setText(splitUserId.name); } else { holder.vName.setText(R.string.user_id_no_name); } - if (splitUserId[1] != null) { - holder.vAddress.setText(splitUserId[1]); + if (splitUserId.email != null) { + holder.vAddress.setText(splitUserId.email); holder.vAddress.setVisibility(View.VISIBLE); } else { holder.vAddress.setVisibility(View.GONE); } - if (splitUserId[2] != null) { - holder.vComment.setText(splitUserId[2]); + if (splitUserId.comment != null) { + holder.vComment.setText(splitUserId.comment); holder.vComment.setVisibility(View.VISIBLE); } else { holder.vComment.setVisibility(View.GONE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java index 5dd675fd3..fe4ba0262 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java @@ -100,8 +100,8 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA // return new user id back to activity Bundle data = new Bundle(); - String userId = KeyRing.createUserId(mName.getText().toString(), - mEmail.getText().toString(), mComment.getText().toString()); + String userId = KeyRing.createUserId(new KeyRing.UserId(mName.getText().toString(), + mEmail.getText().toString(), mComment.getText().toString())); data.putString(MESSAGE_DATA_USER_ID, userId); sendMessageToHandler(MESSAGE_OKAY, data); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index 20f20c32e..f512ecca2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -100,9 +100,9 @@ public class DeleteKeyDialogFragment extends DialogFragment { } ); String name; - String[] mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID)); - if (mainUserId[0] != null) { - name = mainUserId[0]; + KeyRing.UserId mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID)); + if (mainUserId.name != null) { + name = mainUserId.name; } else { name = getString(R.string.user_id_no_name); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java index 7ac85781f..63b6d26ac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java @@ -190,7 +190,7 @@ public class FileDialogFragment extends DialogFragment { mFile = file; mFilename.setText(mFile.getName()); } else { - Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR); + Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java index b34dc2edc..947c316e0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java @@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { private static final String ARG_MESSENGER = "messenger"; @@ -67,12 +68,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi * @param messenger to communicate back after setting the passphrase * @return */ - public static SetPassphraseDialogFragment newInstance(Messenger messenger, String oldPassphrase, int title) { + public static SetPassphraseDialogFragment newInstance(Messenger messenger, Passphrase oldPassphrase, int title) { SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment(); Bundle args = new Bundle(); args.putInt(ARG_TITLE, title); args.putParcelable(ARG_MESSENGER, messenger); - args.putString(ARG_OLD_PASSPHRASE, oldPassphrase); + args.putParcelable(ARG_OLD_PASSPHRASE, oldPassphrase); frag.setArguments(args); @@ -88,7 +89,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi int title = getArguments().getInt(ARG_TITLE); mMessenger = getArguments().getParcelable(ARG_MESSENGER); - String oldPassphrase = getArguments().getString(ARG_OLD_PASSPHRASE); + Passphrase oldPassphrase = getArguments().getParcelable(ARG_OLD_PASSPHRASE); CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); @@ -103,7 +104,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi mNoPassphraseCheckBox = (CheckBox) view.findViewById(R.id.passphrase_no_passphrase); - if (TextUtils.isEmpty(oldPassphrase)) { + if (oldPassphrase.isEmpty()) { mNoPassphraseCheckBox.setChecked(true); mPassphraseEditText.setEnabled(false); mPassphraseAgainEditText.setEnabled(false); @@ -123,12 +124,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi public void onClick(DialogInterface dialog, int id) { dismiss(); - String passphrase1; + Passphrase passphrase1 = new Passphrase(); if (mNoPassphraseCheckBox.isChecked()) { - passphrase1 = ""; + passphrase1.setEmpty(); } else { - passphrase1 = mPassphraseEditText.getText().toString(); - String passphrase2 = mPassphraseAgainEditText.getText().toString(); + passphrase1 = new Passphrase(mPassphraseEditText); + Passphrase passphrase2 = new Passphrase(mPassphraseAgainEditText); if (!passphrase1.equals(passphrase2)) { Toast.makeText( activity, @@ -139,7 +140,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi return; } - if (passphrase1.equals("")) { + if (passphrase1.isEmpty()) { Toast.makeText( activity, getString(R.string.error_message, @@ -152,7 +153,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi // return resulting data back to activity Bundle data = new Bundle(); - data.putString(MESSAGE_NEW_PASSPHRASE, passphrase1); + data.putParcelable(MESSAGE_NEW_PASSPHRASE, passphrase1); sendMessageToHandler(MESSAGE_OKAY, data); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java index 2e6181f07..c0e2fd29c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java @@ -118,7 +118,7 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment private void proofToClipboard() { ClipboardReflection.copyToClipboard(getActivity(), mResourceString); - Notify.showNotify(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK); + Notify.create(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK).show(); } private void saveFile(Uri uri) { @@ -127,10 +127,10 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); out.print(mResourceString); if (out.checkError()) { - Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + Notify.create(getActivity(), "Error writing file!", Style.ERROR).show(); } } catch (FileNotFoundException e) { - Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); + Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show(); e.printStackTrace(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java index 28fbe6f19..99e770857 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java @@ -168,7 +168,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { private void startCertify() { if (mVerifiedResource == null) { - Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR); + Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR).show(); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java index 86a514fa9..77eccf3be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java @@ -80,14 +80,14 @@ public class LinkedIdCreateGithubStep1Fragment extends Fragment { super.onPostExecute(result); if (result == null) { - Notify.showNotify(getActivity(), - "Connection error while checking username!", Notify.Style.ERROR); + Notify.create(getActivity(), + "Connection error while checking username!", Notify.Style.ERROR).show(); return; } if (!result) { - Notify.showNotify(getActivity(), - "This handle does not exist on Github!", Notify.Style.ERROR); + Notify.create(getActivity(), + "This handle does not exist on Github!", Notify.Style.ERROR).show(); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java index 55ac6e075..5559c0daf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java @@ -125,7 +125,7 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen private void proofSave () { String state = Environment.getExternalStorageState(); if (!Environment.MEDIA_MOUNTED.equals(state)) { - Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); + Notify.create(getActivity(), "External storage not available!", Style.ERROR); return; } @@ -146,11 +146,10 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); out.print(mResourceString); if (out.checkError()) { - Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + Notify.create(getActivity(), "Error writing file!", Style.ERROR).show(); } } catch (FileNotFoundException e) { - Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); - e.printStackTrace(); + Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java index c22351f87..c36f98058 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java @@ -78,13 +78,13 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { super.onPostExecute(result); if (result == null) { - Notify.showNotify(getActivity(), + Notify.create(getActivity(), "Connection error while checking username!", Notify.Style.ERROR); return; } if (!result) { - Notify.showNotify(getActivity(), + Notify.create(getActivity(), "This handle does not exist on Twitter!", Notify.Style.ERROR); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java index 502abe8e6..41d0178a3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java @@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.widget.CertListWidget; import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; public class LinkedIdViewFragment extends Fragment implements @@ -153,7 +154,7 @@ public class LinkedIdViewFragment extends Fragment implements } catch (IOException e) { Log.e(Constants.TAG, "error parsing identity", e); - Notify.createNotify(getActivity(), "Error parsing identity!", + Notify.create(getActivity(), "Error parsing identity!", Notify.LENGTH_LONG, Style.ERROR).show(); finishFragment(); } @@ -492,7 +493,7 @@ public class LinkedIdViewFragment extends Fragment implements } // get the user's passphrase for this key (if required) - String passphrase; + Passphrase passphrase; long certifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId(); try { passphrase = PassphraseCacheService.getCachedPassphrase( diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java index 3bc29edb6..7e07ed818 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java @@ -18,7 +18,9 @@ package org.sufficientlysecure.keychain.ui.util; import android.app.Activity; -import android.content.res.Resources; +import android.support.v4.app.Fragment; +import android.view.View; +import android.view.ViewGroup; import com.nispok.snackbar.Snackbar; import com.nispok.snackbar.Snackbar.SnackbarDuration; @@ -35,157 +37,147 @@ import org.sufficientlysecure.keychain.util.FabContainer; */ public class Notify { - public static enum Style {OK, WARN, INFO, ERROR} + public static enum Style { + OK, WARN, ERROR; + + public void applyToBar(Snackbar bar) { + + switch (this) { + case OK: + // bar.actionColorResource(R.color.android_green_light); + bar.lineColorResource(R.color.android_green_light); + break; + case WARN: + // bar.textColorResource(R.color.android_orange_light); + bar.lineColorResource(R.color.android_orange_light); + break; + case ERROR: + // bar.textColorResource(R.color.android_red_light); + bar.lineColorResource(R.color.android_red_light); + break; + } + + } + } public static final int LENGTH_INDEFINITE = 0; public static final int LENGTH_LONG = 3500; - /** - * Shows a simple in-layout notification with the CharSequence given as parameter - * @param text Text to show - * @param style Notification styling - */ - public static void showNotify(final Activity activity, CharSequence text, Style style) { - - Snackbar bar = getSnackbar(activity) + public static Showable create(final Activity activity, String text, int duration, Style style, + final ActionListener actionListener, int actionResId) { + final Snackbar snackbar = Snackbar.with(activity) + .type(SnackbarType.MULTI_LINE) .text(text); - switch (style) { - case OK: - break; - case WARN: - bar.textColor(activity.getResources().getColor(R.color.android_orange_light)); - break; - case ERROR: - bar.textColor(activity.getResources().getColor(R.color.android_red_light)); - break; - } - - SnackbarManager.show(bar); - - } - - public static Showable createNotify (Activity activity, int resId, int duration, Style style) { - final Snackbar bar = getSnackbar(activity) - .text(resId); - if (duration == LENGTH_INDEFINITE) { - bar.duration(SnackbarDuration.LENGTH_INDEFINITE); + snackbar.duration(SnackbarDuration.LENGTH_INDEFINITE); } else { - bar.duration(duration); + snackbar.duration(duration); } - switch (style) { - case OK: - bar.actionColor(activity.getResources().getColor(R.color.android_green_light)); - break; - case WARN: - bar.textColor(activity.getResources().getColor(R.color.android_orange_light)); - break; - case ERROR: - bar.textColor(activity.getResources().getColor(R.color.android_red_light)); - break; - } - - return new Showable () { - @Override - public void show() { - SnackbarManager.show(bar); - } - }; - } - - public static Showable createNotify(Activity activity, int resId, int duration, Style style, - final ActionListener listener, int resIdAction) { - return createNotify(activity, activity.getString(resId), duration, style, listener, resIdAction); - } - - public static Showable createNotify(Activity activity, String msg, int duration, Style style) { - return createNotify(activity, msg, duration, style, null, 0); - } + style.applyToBar(snackbar); - public static Showable createNotify(Activity activity, String msg, int duration, Style style, - final ActionListener listener, int resIdAction) { + if (actionListener != null) { + snackbar.actionLabel(actionResId) + .actionListener(new ActionClickListener() { + @Override + public void onActionClicked(Snackbar snackbar) { + actionListener.onAction(); + } + }); + } - final Snackbar bar = getSnackbar(activity) - .text(msg); + if (activity instanceof FabContainer) { + snackbar.eventListener(new EventListenerAdapter() { + @Override + public void onShow(Snackbar snackbar) { + ((FabContainer) activity).fabMoveUp(snackbar.getHeight()); + } - if (listener != null) { - bar.actionLabel(resIdAction); - bar.actionListener(new ActionClickListener() { @Override - public void onActionClicked(Snackbar snackbar) { - listener.onAction(); + public void onDismiss(Snackbar snackbar) { + ((FabContainer) activity).fabRestorePosition(); } }); } - if (duration == LENGTH_INDEFINITE) { - bar.duration(SnackbarDuration.LENGTH_INDEFINITE); - } else { - bar.duration(duration); - } + return new Showable() { + @Override + public void show() { + SnackbarManager.show(snackbar, activity); + } - switch (style) { - case OK: - bar.actionColor(activity.getResources().getColor(R.color.android_green_light)); - break; - case WARN: - bar.textColor(activity.getResources().getColor(R.color.android_orange_light)); - break; - case ERROR: - bar.textColor(activity.getResources().getColor(R.color.android_red_light)); - break; - } + @Override + public void show(Fragment fragment) { + if (fragment != null) { + View view = fragment.getView(); + + if (view != null && view instanceof ViewGroup) { + SnackbarManager.show(snackbar, (ViewGroup) view); + return; + } + } + + show(); + } - return new Showable () { @Override - public void show() { - SnackbarManager.show(bar); + public void show(ViewGroup viewGroup) { + if (viewGroup != null) { + SnackbarManager.show(snackbar, viewGroup); + return; + } + + show(); } }; + } + public static Showable create(Activity activity, String text, int duration, Style style) { + return create(activity, text, duration, style, null, -1); } - /** - * Shows a simple in-layout notification with the resource text from given id - * @param resId ResourceId of notification text - * @param style Notification styling - * @throws Resources.NotFoundException - */ - public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException { - showNotify(activity, activity.getResources().getText(resId), style); + public static Showable create(Activity activity, String text, Style style) { + return create(activity, text, LENGTH_LONG, style); } - private static Snackbar getSnackbar(final Activity activity) { - Snackbar bar = Snackbar.with(activity) - .type(SnackbarType.MULTI_LINE) - .duration(SnackbarDuration.LENGTH_LONG); + public static Showable create(Activity activity, int textResId, int duration, Style style, + ActionListener actionListener, int actionResId) { + return create(activity, activity.getString(textResId), duration, style, actionListener, actionResId); + } - if (activity instanceof FabContainer) { - bar.eventListener(new EventListenerAdapter() { - @Override - public void onShow(Snackbar snackbar) { - ((FabContainer) activity).fabMoveUp(snackbar.getHeight()); - } + public static Showable create(Activity activity, int textResId, int duration, Style style) { + return create(activity, activity.getString(textResId), duration, style); + } - @Override - public void onDismiss(Snackbar snackbar) { - ((FabContainer) activity).fabRestorePosition(); - } - }); - } - return bar; + public static Showable create(Activity activity, int textResId, Style style) { + return create(activity, activity.getString(textResId), style); } public interface Showable { + + /** + * Shows the notification on the bottom of the Activity. + */ public void show(); + /** + * Shows the notification on the bottom of the Fragment. + */ + public void show(Fragment fragment); + + /** + * Shows the notification on the given ViewGroup. + * The viewGroup should be either a RelativeLayout or FrameLayout. + */ + public void show(ViewGroup viewGroup); + } public interface ActionListener { + public void onAction(); } -}
\ No newline at end of file +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java index 1bdec7b84..e21c5d510 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java @@ -58,9 +58,10 @@ public class EmailEditText extends AutoCompleteTextView { } private void init() { - this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); - this.addTextChangedListener(textWatcher); - removeFlag(); + setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); + reenableKeyboardSuggestions(); + + addTextChangedListener(textWatcher); initAdapter(); } @@ -104,7 +105,7 @@ public class EmailEditText extends AutoCompleteTextView { * Hack to re-enable keyboard auto correction in AutoCompleteTextView. * From http://stackoverflow.com/a/22512858 */ - private void removeFlag() { + private void reenableKeyboardSuggestions() { int inputType = getInputType(); inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; setRawInputType(inputType); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index 94a321f29..ceace1d26 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -184,7 +184,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView { public class EncryptionKey { private String mUserIdFull; - private String[] mUserId; + private KeyRing.UserId mUserId; private long mKeyId; private boolean mHasDuplicate; private Date mCreation; @@ -222,23 +222,23 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView { } public String getPrimary() { - if (mUserId[0] != null) { - return mUserId[0]; + if (mUserId.name != null) { + return mUserId.name; } else { - return mUserId[1]; + return mUserId.email; } } public String getSecondary() { - if (mUserId[1] != null) { - return mUserId[1]; + if (mUserId.email != null) { + return mUserId.email; } else { return getCreationDate(); } } public String getTertiary() { - if (mUserId[0] != null) { + if (mUserId.name != null) { return getCreationDate(); } else { return null; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index aeb013c71..70f4e7792 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -39,7 +39,6 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; import java.util.Calendar; @@ -158,9 +157,9 @@ public abstract class KeySpinner extends TintSpinner implements LoaderManager.Lo TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email); TextView vDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate); - String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId)); - vKeyName.setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")")); - vKeyEmail.setText(userId[1]); + KeyRing.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexUserId)); + vKeyName.setText(userId.name); + vKeyEmail.setText(userId.email); boolean duplicate = cursor.getLong(mIndexDuplicate) > 0; if (duplicate) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java index f086c5696..153bf2ff2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java @@ -50,7 +50,7 @@ public class NameEditText extends AutoCompleteTextView { } private void init() { - removeFlag(); + reenableKeyboardSuggestions(); initAdapter(); } @@ -62,10 +62,10 @@ public class NameEditText extends AutoCompleteTextView { } /** - * Hack to re-enable keyboard auto correction in AutoCompleteTextView. + * Hack to re-enable keyboard suggestions in AutoCompleteTextView. * From http://stackoverflow.com/a/22512858 */ - private void removeFlag() { + private void reenableKeyboardSuggestions() { int inputType = getInputType(); inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; setRawInputType(inputType); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java index 6efc0a5ea..c782d2507 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java @@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.util; import android.accounts.Account; import android.accounts.AccountManager; -import android.annotation.TargetApi; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.ContentUris; @@ -28,7 +27,6 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; -import android.os.Build; import android.provider.ContactsContract; import android.util.Patterns; @@ -37,7 +35,6 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import java.io.InputStream; import java.util.ArrayList; @@ -303,10 +300,9 @@ public class ContactHelper { return new ArrayList<>(names); } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public static Uri dataUriFromContactUri(Context context, Uri contactUri) { Cursor contactMasterKey = context.getContentResolver().query(contactUri, - new String[]{ContactsContract.Data.DATA2}, null, null, null, null); + new String[]{ContactsContract.Data.DATA2}, null, null, null); if (contactMasterKey != null) { if (contactMasterKey.moveToNext()) { return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0)); @@ -447,7 +443,7 @@ public class ContactHelper { if (cursor != null) { while (cursor.moveToNext()) { long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); - String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0; boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; @@ -470,19 +466,19 @@ public class ContactHelper { if (rawContactId != -1) { deleteRawContactById(resolver, rawContactId); } - } else if (userIdSplit[0] != null) { + } else if (userIdSplit.name != null) { // Create a new rawcontact with corresponding key if it does not exist yet if (rawContactId == -1) { Log.d(Constants.TAG, "Insert new raw contact with masterKeyId " + masterKeyId); insertContact(ops, context, masterKeyId); - writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]); + writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit.name); } // We always update the display name (which is derived from primary user id) // and email addresses from user id - writeContactDisplayName(ops, rawContactId, userIdSplit[0]); + writeContactDisplayName(ops, rawContactId, userIdSplit.name); writeContactEmail(ops, resolver, rawContactId, masterKeyId); try { resolver.applyBatch(ContactsContract.AUTHORITY, ops); @@ -521,9 +517,9 @@ public class ContactHelper { long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0; boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID)); - if (!isExpired && !isRevoked && userIdSplit[0] != null) { + if (!isExpired && !isRevoked && userIdSplit.name != null) { // if expired or revoked will not be removed from keysToDelete or inserted // into main profile ("me" contact) boolean existsInMainProfile = keysToDelete.remove(masterKeyId); @@ -534,7 +530,7 @@ public class ContactHelper { ArrayList<ContentProviderOperation> ops = new ArrayList<>(); insertMainProfileRawContact(ops, masterKeyId); - writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]); + writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit.name); try { resolver.applyBatch(ContactsContract.AUTHORITY, ops); @@ -715,7 +711,6 @@ public class ContactHelper { * * @return raw contact id or -1 if not found */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private static long findRawContactId(ContentResolver resolver, long masterKeyId) { long rawContactId = -1; Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, @@ -725,7 +720,7 @@ public class ContactHelper { ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?", new String[]{ Constants.ACCOUNT_TYPE, Long.toString(masterKeyId) - }, null, null); + }, null); if (raw != null) { if (raw.moveToNext()) { rawContactId = raw.getLong(0); @@ -776,14 +771,14 @@ public class ContactHelper { null, null); if (ids != null) { while (ids.moveToNext()) { - String[] userId = KeyRing.splitUserId(ids.getString(0)); - if (userId[1] != null) { + KeyRing.UserId userId = KeyRing.splitUserId(ids.getString(0)); + if (userId.email != null) { ops.add(referenceRawContact( ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI), rawContactId) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Email.DATA, userId[1]) + .withValue(ContactsContract.CommonDataKinds.Email.DATA, userId.email) .build()); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java index cda5892fe..7b164f2b2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java @@ -47,21 +47,6 @@ public class ExportHelper { this.mActivity = activity; } - public void deleteKey(Uri dataUri, Handler deleteHandler) { - try { - long masterKeyId = new ProviderHelper(mActivity).getCachedPublicKeyRing(dataUri) - .extractOrGetMasterKeyId(); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(deleteHandler); - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - new long[]{ masterKeyId }); - deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog"); - } catch (PgpKeyNotFoundException e) { - Log.e(Constants.TAG, "key not found!", e); - } - } - /** * Show dialog where to export keys */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java new file mode 100644 index 000000000..e4e4e4d05 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2015 Kent Nguyen <kentnguyen@moneylover.me> + * + * 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.util; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.nfc.NdefMessage; +import android.nfc.NdefRecord; +import android.nfc.NfcAdapter; +import android.nfc.NfcEvent; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.provider.Settings; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.util.Notify; + +import java.lang.ref.WeakReference; + +/** + * This class contains NFC functionality that can be shared across Fragments or Activities. + */ + +public class NfcHelper { + + private Activity mActivity; + private ProviderHelper mProviderHelper; + + /** + * NFC: This handler receives a message from onNdefPushComplete + */ + private static NfcHandler mNfcHandler; + + private NfcAdapter mNfcAdapter; + private NfcAdapter.CreateNdefMessageCallback mNdefCallback; + private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback; + private byte[] mNfcKeyringBytes; + private static final int NFC_SENT = 1; + + /** + * Initializes the NfcHelper. + */ + public NfcHelper(final Activity activity, final ProviderHelper providerHelper) { + mActivity = activity; + mProviderHelper = providerHelper; + + mNfcHandler = new NfcHandler(mActivity); + } + + /** + * Return true if the NFC Adapter of this Helper has any features enabled. + * + * @return true if this NFC Adapter has any features enabled + */ + public boolean isEnabled() { + return mNfcAdapter.isEnabled(); + } + + /** + * NFC: Initialize NFC sharing if OS and device supports it + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + public void initNfc(final Uri dataUri) { + // check if NFC Beam is supported (>= Android 4.1) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + + // Implementation for the CreateNdefMessageCallback interface + mNdefCallback = new NfcAdapter.CreateNdefMessageCallback() { + @Override + public NdefMessage createNdefMessage(NfcEvent event) { + /* + * When a device receives a push with an AAR in it, the application specified in the AAR is + * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to + * guarantee that this activity starts when receiving a beamed message. For now, this code + * uses the tag dispatch system. + */ + return new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME, + mNfcKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME)); + } + }; + + // Implementation for the OnNdefPushCompleteCallback interface + mNdefCompleteCallback = new NfcAdapter.OnNdefPushCompleteCallback() { + @Override + public void onNdefPushComplete(NfcEvent event) { + // A handler is needed to send messages to the activity when this + // callback occurs, because it happens from a binder thread + mNfcHandler.obtainMessage(NFC_SENT).sendToTarget(); + } + }; + + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity); + if (mNfcAdapter != null) { + /* + * Retrieve mNfcKeyringBytes here asynchronously (to not block the UI) + * and init nfc adapter afterwards. + * mNfcKeyringBytes can not be retrieved in createNdefMessage, because this process + * has no permissions to query the Uri. + */ + AsyncTask<Void, Void, Void> initTask = + new AsyncTask<Void, Void, Void>() { + protected Void doInBackground(Void... unused) { + try { + Uri blobUri = + KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri); + mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData( + blobUri, + KeychainContract.KeyRingData.KEY_RING_DATA, + ProviderHelper.FIELD_TYPE_BLOB); + } catch (ProviderHelper.NotFoundException e) { + Log.e(Constants.TAG, "key not found!", e); + } + + // no AsyncTask return (Void) + return null; + } + + protected void onPostExecute(Void unused) { + // Register callback to set NDEF message + mNfcAdapter.setNdefPushMessageCallback(mNdefCallback, + mActivity); + // Register callback to listen for message-sent success + mNfcAdapter.setOnNdefPushCompleteCallback(mNdefCompleteCallback, + mActivity); + } + }; + + initTask.execute(); + } + } + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public void invokeNfcBeam() { + // Check if device supports NFC + if (!mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { + Notify.create(mActivity, R.string.no_nfc_support, Notify.LENGTH_LONG, Notify.Style.ERROR).show(); + return; + } + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity); + if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { + Notify.create(mActivity, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS); + mActivity.startActivity(intentSettings); + } + }, R.string.menu_nfc_preferences).show(); + + return; + } + + if (!mNfcAdapter.isNdefPushEnabled()) { + Notify.create(mActivity, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS); + mActivity.startActivity(intentSettings); + } + }, R.string.menu_beam_preferences).show(); + + return; + } + + mNfcAdapter.invokeBeam(mActivity); + } + + /** + * A static subclass of {@link Handler} with a {@link WeakReference} to an {@link Activity} to avoid memory leaks. + */ + private static class NfcHandler extends Handler { + private final WeakReference<Activity> mActivityReference; + + public NfcHandler(Activity activity) { + mActivityReference = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + Activity activity = mActivityReference.get(); + + if (activity != null) { + switch (msg.what) { + case NFC_SENT: + Notify.create(activity, R.string.nfc_successful, Notify.Style.OK).show(); + break; + } + } + } + } + +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java new file mode 100644 index 000000000..06efdde4d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2015 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.util; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.Editable; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.Constants; + +import java.util.Arrays; + +/** + * Passwords should not be stored as Strings in memory. + * This class wraps a char[] that can be erased after it is no longer used. + * See also: + * <p/> + * http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#PBEEx + * https://github.com/c-a-m/passfault/blob/master/core/src/main/java/org/owasp/passfault/SecureString.java + * http://stackoverflow.com/q/8881291 + * http://stackoverflow.com/a/15844273 + */ +public class Passphrase implements Parcelable { + private char[] mPassphrase; + + /** + * According to http://stackoverflow.com/a/15844273 EditText is not using String internally + * but char[]. Thus, we can get the char[] directly from it. + */ + public Passphrase(Editable editable) { + int pl = editable.length(); + mPassphrase = new char[pl]; + editable.getChars(0, pl, mPassphrase, 0); + // TODO: clean up internal char[] of EditText after getting the passphrase? +// editText.getText().replace() + } + + public Passphrase(EditText editText) { + this(editText.getText()); + } + + public Passphrase(char[] passphrase) { + mPassphrase = passphrase; + } + + public Passphrase(String passphrase) { + mPassphrase = passphrase.toCharArray(); + } + + /** + * Creates a passphrase object with an empty ("") passphrase + */ + public Passphrase() { + setEmpty(); + } + + public char[] getCharArray() { + return mPassphrase; + } + + public void setEmpty() { + removeFromMemory(); + mPassphrase = new char[0]; + } + + public boolean isEmpty() { + return (length() == 0); + } + + public int length() { + return mPassphrase.length; + } + + public char charAt(int index) { + return mPassphrase[index]; + } + + /** + * Manually clear the underlying array holding the characters + */ + public void removeFromMemory() { + if (mPassphrase != null) { + Arrays.fill(mPassphrase, ' '); + } + } + + @Override + public void finalize() throws Throwable { + removeFromMemory(); + super.finalize(); + } + + @Override + public String toString() { + if (Constants.DEBUG) { + return "Passphrase{" + + "mPassphrase=" + Arrays.toString(mPassphrase) + + '}'; + } else { + return "Passphrase: hidden"; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Passphrase that = (Passphrase) o; + if (!Arrays.equals(mPassphrase, that.mPassphrase)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return mPassphrase != null ? Arrays.hashCode(mPassphrase) : 0; + } + + private Passphrase(Parcel source) { + mPassphrase = source.createCharArray(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeCharArray(mPassphrase); + } + + public static final Creator<Passphrase> CREATOR = new Creator<Passphrase>() { + public Passphrase createFromParcel(final Parcel source) { + return new Passphrase(source); + } + + public Passphrase[] newArray(final int size) { + return new Passphrase[size]; + } + }; + + public int describeContents() { + return 0; + } +} |