diff options
author | Vincent Breitmoser <valodim@mugenguild.com> | 2015-01-31 18:49:54 +0100 |
---|---|---|
committer | Vincent Breitmoser <valodim@mugenguild.com> | 2015-01-31 18:49:54 +0100 |
commit | 0b6dc65c97b0fb5dae9bf1a06f9c7db0b65ea4ad (patch) | |
tree | 92954cab7f0aa299c2658a9f15586168ccf12e4f /OpenKeychain/src/main/java | |
parent | 5466d1e9802815726d713bca2aeabaff2861519c (diff) | |
parent | e651a392795caa395b060946b0cfaca5a5b41ded (diff) | |
download | open-keychain-0b6dc65c97b0fb5dae9bf1a06f9c7db0b65ea4ad.tar.gz open-keychain-0b6dc65c97b0fb5dae9bf1a06f9c7db0b65ea4ad.tar.bz2 open-keychain-0b6dc65c97b0fb5dae9bf1a06f9c7db0b65ea4ad.zip |
Merge remote-tracking branch 'origin/development' into development
Diffstat (limited to 'OpenKeychain/src/main/java')
17 files changed, 985 insertions, 973 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index b3b433a63..67fa30a44 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -20,10 +20,6 @@ package org.sufficientlysecure.keychain; import android.os.Environment; import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.sufficientlysecure.keychain.ui.DecryptActivity; -import org.sufficientlysecure.keychain.ui.EncryptFilesActivity; -import org.sufficientlysecure.keychain.ui.EncryptTextActivity; -import org.sufficientlysecure.keychain.ui.MainActivity; import java.io.File; @@ -90,21 +86,6 @@ public final class Constants { public static final int PREF_VERSION = 4; } - public static final class DrawerItems { - public static final Class KEY_LIST = MainActivity.class; - public static final Class ENCRYPT_TEXT = EncryptTextActivity.class; - public static final Class ENCRYPT_FILE = EncryptFilesActivity.class; - public static final Class DECRYPT = DecryptActivity.class; - public static final Class REGISTERED_APPS_LIST = MainActivity.class; - public static final Class[] ARRAY = new Class[]{ - KEY_LIST, - ENCRYPT_TEXT, - ENCRYPT_FILE, - DECRYPT, - REGISTERED_APPS_LIST - }; - } - public static final class key { public static final int none = 0; public static final int symmetric = -1; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index f4e00c36c..5856589c4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -85,6 +85,11 @@ public class KeychainContract { String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name } + interface ApiAppsAllowedKeysColumns { + String KEY_ID = "key_id"; // not a database id + String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name + } + public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME + ".provider"; private static final Uri BASE_CONTENT_URI_INTERNAL = Uri @@ -106,6 +111,7 @@ public class KeychainContract { public static final String BASE_API_APPS = "api_apps"; public static final String PATH_ACCOUNTS = "accounts"; + public static final String PATH_ALLOWED_KEYS = "allowed_keys"; public static class KeyRings implements BaseColumns, KeysColumns, UserPacketsColumns { public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID; @@ -305,6 +311,22 @@ public class KeychainContract { } } + public static class ApiAllowedKeys implements ApiAppsAllowedKeysColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_API_APPS).build(); + + /** + * Use if multiple items get returned + */ + public static final String CONTENT_TYPE + = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.api_apps.allowed_keys"; + + public static Uri buildBaseUri(String packageName) { + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ALLOWED_KEYS) + .build(); + } + } + public static class Certs implements CertsColumns, BaseColumns { public static final String USER_ID = UserPacketsColumns.USER_ID; public static final String SIGNER_UID = "signer_user_id"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 5ce5eec17..d34cc74a3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -28,6 +28,7 @@ import android.provider.BaseColumns; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAllowedKeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; @@ -52,7 +53,7 @@ import java.io.IOException; */ public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 7; + private static final int DATABASE_VERSION = 8; static Boolean apgHack = false; private Context mContext; @@ -64,6 +65,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { String CERTS = "certs"; String API_APPS = "api_apps"; String API_ACCOUNTS = "api_accounts"; + String API_ALLOWED_KEYS = "api_allowed_keys"; } private static final String CREATE_KEYRINGS_PUBLIC = @@ -166,6 +168,18 @@ public class KeychainDatabase extends SQLiteOpenHelper { + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE" + ")"; + private static final String CREATE_API_APPS_ALLOWED_KEYS = + "CREATE TABLE IF NOT EXISTS " + Tables.API_ALLOWED_KEYS + " (" + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + ApiAppsAllowedKeysColumns.KEY_ID + " INTEGER, " + + ApiAppsAllowedKeysColumns.PACKAGE_NAME + " TEXT NOT NULL, " + + + "UNIQUE(" + ApiAppsAllowedKeysColumns.KEY_ID + ", " + + ApiAppsAllowedKeysColumns.PACKAGE_NAME + "), " + + "FOREIGN KEY(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") REFERENCES " + + Tables.API_APPS + "(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") ON DELETE CASCADE" + + ")"; + KeychainDatabase(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); mContext = context; @@ -195,6 +209,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL(CREATE_CERTS); db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS_ACCOUNTS); + db.execSQL(CREATE_API_APPS_ALLOWED_KEYS); } @Override @@ -243,6 +258,15 @@ public class KeychainDatabase extends SQLiteOpenHelper { case 6: db.execSQL("ALTER TABLE user_ids ADD COLUMN type INTEGER"); db.execSQL("ALTER TABLE user_ids ADD COLUMN attribute_data BLOB"); + case 7: + // consolidate + case 8: + // new table for allowed key ids in API + try { + db.execSQL(CREATE_API_APPS_ALLOWED_KEYS); + } catch (Exception e) { + // never mind, the column probably already existed + } } // always do consolidate after upgrade diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 72475472e..2ffc8c2ca 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -31,6 +31,7 @@ import android.net.Uri; import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; @@ -63,9 +64,10 @@ public class KeychainProvider extends ContentProvider { private static final int KEY_RING_CERTS_SPECIFIC = 206; private static final int API_APPS = 301; - private static final int API_APPS_BY_PACKAGE_NAME = 303; - private static final int API_ACCOUNTS = 304; - private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306; + private static final int API_APPS_BY_PACKAGE_NAME = 302; + private static final int API_ACCOUNTS = 303; + private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 304; + private static final int API_ALLOWED_KEYS = 305; private static final int KEY_RINGS_FIND_BY_EMAIL = 400; private static final int KEY_RINGS_FIND_BY_SUBKEY = 401; @@ -162,6 +164,8 @@ public class KeychainProvider extends ContentProvider { * * api_apps/_/accounts * api_apps/_/accounts/_ (account name) + * + * api_apps/_/allowed_keys * </pre> */ matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS); @@ -172,6 +176,9 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" + KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" + + KeychainContract.PATH_ALLOWED_KEYS, API_ALLOWED_KEYS); + return matcher; } @@ -223,6 +230,9 @@ public class KeychainProvider extends ContentProvider { case API_ACCOUNTS_BY_ACCOUNT_NAME: return ApiAccounts.CONTENT_ITEM_TYPE; + case API_ALLOWED_KEYS: + return ApiAllowedKeys.CONTENT_TYPE; + default: throw new UnsupportedOperationException("Unknown uri: " + uri); } @@ -614,6 +624,12 @@ public class KeychainProvider extends ContentProvider { qb.appendWhereEscapeString(uri.getLastPathSegment()); break; + case API_ALLOWED_KEYS: + qb.setTables(Tables.API_ALLOWED_KEYS); + qb.appendWhere(Tables.API_ALLOWED_KEYS + "." + ApiAccounts.PACKAGE_NAME + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); + + break; default: throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")"); @@ -683,7 +699,7 @@ public class KeychainProvider extends ContentProvider { )) { throw new AssertionError("Incorrect type for user packet! This is a bug!"); } - if (values.get(UserPacketsColumns.RANK) == 0 && values.get(UserPacketsColumns.USER_ID) == null) { + if (((Number)values.get(UserPacketsColumns.RANK)).intValue() == 0 && values.get(UserPacketsColumns.USER_ID) == null) { throw new AssertionError("Rank 0 user packet must be a user id!"); } db.insertOrThrow(Tables.USER_PACKETS, null, values); @@ -701,7 +717,7 @@ public class KeychainProvider extends ContentProvider { db.insertOrThrow(Tables.API_APPS, null, values); break; - case API_ACCOUNTS: + case API_ACCOUNTS: { // set foreign key automatically based on given uri // e.g., api_apps/com.example.app/accounts/ String packageName = uri.getPathSegments().get(1); @@ -709,12 +725,21 @@ public class KeychainProvider extends ContentProvider { db.insertOrThrow(Tables.API_ACCOUNTS, null, values); break; + } + case API_ALLOWED_KEYS: { + // set foreign key automatically based on given uri + // e.g., api_apps/com.example.app/allowed_keys/ + String packageName = uri.getPathSegments().get(1); + values.put(ApiAllowedKeys.PACKAGE_NAME, packageName); + db.insertOrThrow(Tables.API_ALLOWED_KEYS, null, values); + break; + } default: throw new UnsupportedOperationException("Unknown uri: " + uri); } - if(keyId != null) { + if (keyId != null) { uri = KeyRings.buildGenericKeyRingUri(keyId); rowUri = uri; } @@ -777,6 +802,10 @@ public class KeychainProvider extends ContentProvider { count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, additionalSelection), selectionArgs); break; + case API_ALLOWED_KEYS: + count = db.delete(Tables.API_ALLOWED_KEYS, buildDefaultApiAllowedKeysSelection(uri, additionalSelection), + selectionArgs); + break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } @@ -869,4 +898,15 @@ public class KeychainProvider extends ContentProvider { + andSelection; } + private String buildDefaultApiAllowedKeysSelection(Uri uri, String selection) { + String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1)); + + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; + } + + return ApiAllowedKeys.PACKAGE_NAME + "=" + packageName + andSelection; + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index f23006a94..2ba7a19dc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -50,6 +50,7 @@ import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; import org.sufficientlysecure.keychain.pgp.WrappedSignature; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; @@ -1312,7 +1313,6 @@ public class ProviderHelper { progress.setProgress(100, 100); log.add(LogType.MSG_CON_SUCCESS, indent); - indent -= 1; return new ConsolidateResult(ConsolidateResult.RESULT_OK, log); @@ -1508,6 +1508,39 @@ public class ProviderHelper { return keyIds; } + public Set<Long> getAllowedKeyIdsForApp(Uri uri) { + Set<Long> keyIds = new HashSet<>(); + + Cursor cursor = mContentResolver.query(uri, null, null, null, null); + try { + if (cursor != null) { + int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAllowedKeys.KEY_ID); + while (cursor.moveToNext()) { + keyIds.add(cursor.getLong(keyIdColumn)); + } + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + + return keyIds; + } + + public void saveAllowedKeyIdsForApp(Uri uri, Set<Long> allowedKeyIds) + throws RemoteException, OperationApplicationException { + // wipe whole table of allowed keys for this account + mContentResolver.delete(uri, null, null); + + // re-insert allowed key ids + for (Long keyId : allowedKeyIds) { + ContentValues values = new ContentValues(); + values.put(ApiAllowedKeys.KEY_ID, keyId); + mContentResolver.insert(uri, values); + } + } + public Set<String> getAllFingerprints(Uri uri) { Set<String> fingerprints = new HashSet<>(); String[] projection = new String[]{KeyRings.FINGERPRINT}; 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 d967931ce..d4f1e248c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.ui.ViewKeyActivity; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; @@ -224,6 +225,8 @@ public class OpenPgpService extends RemoteService { private Intent signImpl(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output, AccountSettings accSettings, boolean cleartextSign) { + InputStream is = null; + OutputStream os = null; try { boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); @@ -243,96 +246,88 @@ public class OpenPgpService extends RemoteService { } // Get Input- and OutputStream from ParcelFileDescriptor - InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); - OutputStream os = null; + is = new ParcelFileDescriptor.AutoCloseInputStream(input); if (cleartextSign) { // output stream only needed for cleartext signatures, // detached signatures are returned as extra os = new ParcelFileDescriptor.AutoCloseOutputStream(output); } + long inputLength = is.available(); + InputData inputData = new InputData(is, inputLength); + + // Find the appropriate subkey to sign with + long sigSubKeyId; try { - long inputLength = is.available(); - InputData inputData = new InputData(is, inputLength); + CachedPublicKeyRing signingRing = + new ProviderHelper(this).getCachedPublicKeyRing(accSettings.getKeyId()); + sigSubKeyId = signingRing.getSecretSignId(); + } catch (PgpKeyNotFoundException e) { + // secret key that is set for this account is deleted? + // show account config again! + return getCreateAccountIntent(data, getAccountName(data)); + } - // Find the appropriate subkey to sign with - long sigSubKeyId; + // get passphrase from cache, if key has "no" passphrase, this returns an empty String + String passphrase; + if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { + passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); + } else { try { - CachedPublicKeyRing signingRing = - new ProviderHelper(this).getCachedPublicKeyRing(accSettings.getKeyId()); - sigSubKeyId = signingRing.getSecretSignId(); - } catch (PgpKeyNotFoundException e) { - // secret key that is set for this account is deleted? - // show account config again! + passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), + accSettings.getKeyId(), sigSubKeyId); + } catch (PassphraseCacheService.KeyNotFoundException e) { + // should happen earlier, but return again here if it happens return getCreateAccountIntent(data, getAccountName(data)); } + } + if (passphrase == null) { + // get PendingIntent for passphrase input, add it to given params and return to client + return getPassphraseIntent(data, sigSubKeyId); + } - // get passphrase from cache, if key has "no" passphrase, this returns an empty String - String passphrase; - if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { - passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); - } else { - try { - passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), - accSettings.getKeyId(), sigSubKeyId); - } catch (PassphraseCacheService.KeyNotFoundException e) { - // should happen earlier, but return again here if it happens - return getCreateAccountIntent(data, getAccountName(data)); - } - } - if (passphrase == null) { - // get PendingIntent for passphrase input, add it to given params and return to client - return getPassphraseIntent(data, sigSubKeyId); - } - - // sign-only - PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( - this, new ProviderHelper(getContext()), null, - inputData, os - ); - builder.setEnableAsciiArmorOutput(asciiArmor) - .setCleartextSignature(cleartextSign) - .setDetachedSignature(!cleartextSign) - .setVersionHeader(PgpHelper.getVersionForHeader(this)) - .setSignatureHashAlgorithm(accSettings.getHashAlgorithm()) - .setSignatureMasterKeyId(accSettings.getKeyId()) - .setSignatureSubKeyId(sigSubKeyId) - .setSignaturePassphrase(passphrase) - .setNfcState(nfcSignedHash, nfcCreationDate); - - // execute PGP operation! - SignEncryptResult pgpResult = builder.build().execute(); - - if (pgpResult.isPending()) { - if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == - SignEncryptResult.RESULT_PENDING_PASSPHRASE) { - return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded()); - } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == - SignEncryptResult.RESULT_PENDING_NFC) { - // return PendingIntent to execute NFC activity - // pass through the signature creation timestamp to be used again on second execution - // of PgpSignEncrypt when we have the signed hash! - data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); - return getNfcSignIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); - } else { - throw new PgpGeneralException( - "Encountered unhandled type of pending action not supported by API!"); - } - } else if (pgpResult.success()) { - Intent result = new Intent(); - if (!cleartextSign) { - result.putExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE, pgpResult.getDetachedSignature()); - } - result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); - return result; + // sign-only + PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( + this, new ProviderHelper(getContext()), null, + inputData, os + ); + builder.setEnableAsciiArmorOutput(asciiArmor) + .setCleartextSignature(cleartextSign) + .setDetachedSignature(!cleartextSign) + .setVersionHeader(PgpHelper.getVersionForHeader(this)) + .setSignatureHashAlgorithm(accSettings.getHashAlgorithm()) + .setSignatureMasterKeyId(accSettings.getKeyId()) + .setSignatureSubKeyId(sigSubKeyId) + .setSignaturePassphrase(passphrase) + .setNfcState(nfcSignedHash, nfcCreationDate); + + // execute PGP operation! + SignEncryptResult pgpResult = builder.build().execute(); + + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == + SignEncryptResult.RESULT_PENDING_PASSPHRASE) { + return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == + SignEncryptResult.RESULT_PENDING_NFC) { + // return PendingIntent to execute NFC activity + // pass through the signature creation timestamp to be used again on second execution + // of PgpSignEncrypt when we have the signed hash! + data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); + return getNfcSignIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); } else { - LogEntryParcel errorMsg = pgpResult.getLog().getLast(); - throw new Exception(getString(errorMsg.mType.getMsgId())); + throw new PgpGeneralException( + "Encountered unhandled type of pending action not supported by API!"); } - } finally { - is.close(); - if (os != null) { - os.close(); + } else if (pgpResult.success()) { + Intent result = new Intent(); + if (!cleartextSign) { + result.putExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE, pgpResult.getDetachedSignature()); } + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; + } else { + LogEntryParcel errorMsg = pgpResult.getLog().getLast(); + throw new Exception(getString(errorMsg.mType.getMsgId())); } } catch (Exception e) { Log.d(Constants.TAG, "signImpl", e); @@ -341,12 +336,29 @@ public class OpenPgpService extends RemoteService { new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing InputStream", e); + } + } + if (os != null) { + try { + os.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing OutputStream", e); + } + } } } private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output, AccountSettings accSettings, boolean sign) { + InputStream is = null; + OutputStream os = null; try { boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); String originalFilename = data.getStringExtra(OpenPgpApi.EXTRA_ORIGINAL_FILENAME); @@ -372,96 +384,91 @@ public class OpenPgpService extends RemoteService { // build InputData and write into OutputStream // Get Input- and OutputStream from ParcelFileDescriptor - InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); - OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output); - try { - long inputLength = is.available(); - InputData inputData = new InputData(is, inputLength); - - PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( - this, new ProviderHelper(getContext()), null, inputData, os - ); - builder.setEnableAsciiArmorOutput(asciiArmor) - .setVersionHeader(PgpHelper.getVersionForHeader(this)) - .setCompressionId(accSettings.getCompression()) - .setSymmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm()) - .setEncryptionMasterKeyIds(keyIds) - .setFailOnMissingEncryptionKeyIds(true) - .setOriginalFilename(originalFilename) - .setAdditionalEncryptId(accSettings.getKeyId()); // add acc key for encryption - - if (sign) { - - // Find the appropriate subkey to sign with - long sigSubKeyId; - try { - CachedPublicKeyRing signingRing = - new ProviderHelper(this).getCachedPublicKeyRing(accSettings.getKeyId()); - sigSubKeyId = signingRing.getSecretSignId(); - } catch (PgpKeyNotFoundException e) { - // secret key that is set for this account is deleted? - // show account config again! - return getCreateAccountIntent(data, getAccountName(data)); - } + is = new ParcelFileDescriptor.AutoCloseInputStream(input); + os = new ParcelFileDescriptor.AutoCloseOutputStream(output); + + long inputLength = is.available(); + InputData inputData = new InputData(is, inputLength); + + PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( + this, new ProviderHelper(getContext()), null, inputData, os + ); + builder.setEnableAsciiArmorOutput(asciiArmor) + .setVersionHeader(PgpHelper.getVersionForHeader(this)) + .setCompressionId(accSettings.getCompression()) + .setSymmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm()) + .setEncryptionMasterKeyIds(keyIds) + .setFailOnMissingEncryptionKeyIds(true) + .setOriginalFilename(originalFilename) + .setAdditionalEncryptId(accSettings.getKeyId()); // add acc key for encryption + + if (sign) { - String passphrase; - if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { - passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); - } else { - passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), - accSettings.getKeyId(), sigSubKeyId); - } - if (passphrase == null) { - // get PendingIntent for passphrase input, add it to given params and return to client - return getPassphraseIntent(data, sigSubKeyId); - } + // Find the appropriate subkey to sign with + long sigSubKeyId; + try { + CachedPublicKeyRing signingRing = + new ProviderHelper(this).getCachedPublicKeyRing(accSettings.getKeyId()); + sigSubKeyId = signingRing.getSecretSignId(); + } catch (PgpKeyNotFoundException e) { + // secret key that is set for this account is deleted? + // show account config again! + return getCreateAccountIntent(data, getAccountName(data)); + } - byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH); - // carefully: only set if timestamp exists - Date nfcCreationDate = null; - long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1); - if (nfcCreationTimestamp != -1) { - nfcCreationDate = new Date(nfcCreationTimestamp); - } + String passphrase; + if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { + passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); + } else { + passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), + accSettings.getKeyId(), sigSubKeyId); + } + if (passphrase == null) { + // get PendingIntent for passphrase input, add it to given params and return to client + return getPassphraseIntent(data, sigSubKeyId); + } - // sign and encrypt - builder.setSignatureHashAlgorithm(accSettings.getHashAlgorithm()) - .setSignatureMasterKeyId(accSettings.getKeyId()) - .setSignatureSubKeyId(sigSubKeyId) - .setSignaturePassphrase(passphrase) - .setNfcState(nfcSignedHash, nfcCreationDate); + byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH); + // carefully: only set if timestamp exists + Date nfcCreationDate = null; + long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1); + if (nfcCreationTimestamp != -1) { + nfcCreationDate = new Date(nfcCreationTimestamp); } - // execute PGP operation! - SignEncryptResult pgpResult = builder.build().execute(); - - if (pgpResult.isPending()) { - if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == - SignEncryptResult.RESULT_PENDING_PASSPHRASE) { - return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded()); - } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == - SignEncryptResult.RESULT_PENDING_NFC) { - // return PendingIntent to execute NFC activity - // pass through the signature creation timestamp to be used again on second execution - // of PgpSignEncrypt when we have the signed hash! - data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); - return getNfcSignIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); - } else { - throw new PgpGeneralException( - "Encountered unhandled type of pending action not supported by API!"); - } - } else if (pgpResult.success()) { - Intent result = new Intent(); - result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); - return result; + // sign and encrypt + builder.setSignatureHashAlgorithm(accSettings.getHashAlgorithm()) + .setSignatureMasterKeyId(accSettings.getKeyId()) + .setSignatureSubKeyId(sigSubKeyId) + .setSignaturePassphrase(passphrase) + .setNfcState(nfcSignedHash, nfcCreationDate); + } + + // execute PGP operation! + SignEncryptResult pgpResult = builder.build().execute(); + + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == + SignEncryptResult.RESULT_PENDING_PASSPHRASE) { + return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == + SignEncryptResult.RESULT_PENDING_NFC) { + // return PendingIntent to execute NFC activity + // pass through the signature creation timestamp to be used again on second execution + // of PgpSignEncrypt when we have the signed hash! + data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); + return getNfcSignIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); } else { - LogEntryParcel errorMsg = pgpResult.getLog().getLast(); - throw new Exception(getString(errorMsg.mType.getMsgId())); + throw new PgpGeneralException( + "Encountered unhandled type of pending action not supported by API!"); } - - } finally { - is.close(); - os.close(); + } else if (pgpResult.success()) { + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; + } else { + LogEntryParcel errorMsg = pgpResult.getLog().getLast(); + throw new Exception(getString(errorMsg.mType.getMsgId())); } } catch (Exception e) { Log.d(Constants.TAG, "encryptAndSignImpl", e); @@ -470,17 +477,33 @@ public class OpenPgpService extends RemoteService { new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing InputStream", e); + } + } + if (os != null) { + try { + os.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing OutputStream", e); + } + } } } private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output, Set<Long> allowedKeyIds, boolean decryptMetadataOnly) { + InputStream is = null; + OutputStream os = null; try { // Get Input- and OutputStream from ParcelFileDescriptor - InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); + is = new ParcelFileDescriptor.AutoCloseInputStream(input); - OutputStream os; // output is optional, e.g., for verifying detached signatures if (decryptMetadataOnly || output == null) { os = null; @@ -488,101 +511,95 @@ public class OpenPgpService extends RemoteService { os = new ParcelFileDescriptor.AutoCloseOutputStream(output); } - try { - String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); - long inputLength = is.available(); - InputData inputData = new InputData(is, inputLength); - - PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( - this, new ProviderHelper(getContext()), null, inputData, os - ); - - byte[] nfcDecryptedSessionKey = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DECRYPTED_SESSION_KEY); - - byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE); - - // allow only private keys associated with accounts of this app - // no support for symmetric encryption - builder.setPassphrase(passphrase) - .setAllowSymmetricDecryption(false) - .setAllowedKeyIds(allowedKeyIds) - .setDecryptMetadataOnly(decryptMetadataOnly) - .setNfcState(nfcDecryptedSessionKey) - .setDetachedSignature(detachedSignature); - - DecryptVerifyResult pgpResult = builder.build().execute(); - - if (pgpResult.isPending()) { - if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) == - DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) { - return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded()); - } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) == - DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) { - throw new PgpGeneralException( - "Decryption of symmetric content not supported by API!"); - } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) == - DecryptVerifyResult.RESULT_PENDING_NFC) { - return getNfcDecryptIntent( - data, pgpResult.getNfcSubKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcEncryptedSessionKey()); - } else { - throw new PgpGeneralException( - "Encountered unhandled type of pending action not supported by API!"); - } - } else if (pgpResult.success()) { - Intent result = new Intent(); - - OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult(); - if (signatureResult != null) { - result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult); - - if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) { - // SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED have been added in version 5 - if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED - || signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED) { - signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR); - } - } - - if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) { - // If signature is unknown we return an _additional_ PendingIntent - // to retrieve the missing key - Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE); - intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId()); - intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data); + String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); + long inputLength = is.available(); + InputData inputData = new InputData(is, inputLength); + + PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( + this, new ProviderHelper(getContext()), null, inputData, os + ); + + byte[] nfcDecryptedSessionKey = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DECRYPTED_SESSION_KEY); + + byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE); + + // allow only private keys associated with accounts of this app + // no support for symmetric encryption + builder.setPassphrase(passphrase) + .setAllowSymmetricDecryption(false) + .setAllowedKeyIds(allowedKeyIds) + .setDecryptMetadataOnly(decryptMetadataOnly) + .setNfcState(nfcDecryptedSessionKey) + .setDetachedSignature(detachedSignature); + + DecryptVerifyResult pgpResult = builder.build().execute(); + + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) { + return getPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) == + DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) { + throw new PgpGeneralException( + "Decryption of symmetric content not supported by API!"); + } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) == + DecryptVerifyResult.RESULT_PENDING_NFC) { + return getNfcDecryptIntent( + data, pgpResult.getNfcSubKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcEncryptedSessionKey()); + } else { + throw new PgpGeneralException( + "Encountered unhandled type of pending action not supported by API!"); + } + } else if (pgpResult.success()) { + Intent result = new Intent(); - PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, - intent, - PendingIntent.FLAG_CANCEL_CURRENT); + OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult(); + if (signatureResult != null) { + result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult); - result.putExtra(OpenPgpApi.RESULT_INTENT, pi); + if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) { + // SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED have been added in version 5 + if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED + || signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED) { + signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR); } } - if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) { - OpenPgpMetadata metadata = pgpResult.getDecryptMetadata(); - if (metadata != null) { - result.putExtra(OpenPgpApi.RESULT_METADATA, metadata); - } - } + if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) { + // If signature is unknown we return an _additional_ PendingIntent + // to retrieve the missing key + Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class); + intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE); + intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId()); + intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data); - String charset = pgpResult.getCharset(); - if (charset != null) { - result.putExtra(OpenPgpApi.RESULT_CHARSET, charset); + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT); + + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); } + } - result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); - return result; - } else { - LogEntryParcel errorMsg = pgpResult.getLog().getLast(); - throw new Exception(getString(errorMsg.mType.getMsgId())); + if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) { + OpenPgpMetadata metadata = pgpResult.getDecryptMetadata(); + if (metadata != null) { + result.putExtra(OpenPgpApi.RESULT_METADATA, metadata); + } } - } finally { - is.close(); - if (os != null) { - os.close(); + + String charset = pgpResult.getCharset(); + if (charset != null) { + result.putExtra(OpenPgpApi.RESULT_CHARSET, charset); } + + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; + } else { + LogEntryParcel errorMsg = pgpResult.getLog().getLast(); + throw new Exception(getString(errorMsg.mType.getMsgId())); } + } catch (Exception e) { Log.d(Constants.TAG, "decryptAndVerifyImpl", e); Intent result = new Intent(); @@ -590,6 +607,21 @@ public class OpenPgpService extends RemoteService { new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing InputStream", e); + } + } + if (os != null) { + try { + os.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing OutputStream", e); + } + } } } @@ -720,48 +752,66 @@ public class OpenPgpService extends RemoteService { @Override public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) { - Intent errorResult = checkRequirements(data); - if (errorResult != null) { - return errorResult; - } + try { + Intent errorResult = checkRequirements(data); + if (errorResult != null) { + return errorResult; + } - String accName = getAccountName(data); - final AccountSettings accSettings = getAccSettings(accName); - if (accSettings == null) { - return getCreateAccountIntent(data, accName); - } + String accName = getAccountName(data); + final AccountSettings accSettings = getAccSettings(accName); + if (accSettings == null) { + return getCreateAccountIntent(data, accName); + } - String action = data.getAction(); - if (OpenPgpApi.ACTION_CLEARTEXT_SIGN.equals(action)) { - return signImpl(data, input, output, accSettings, true); - } else if (OpenPgpApi.ACTION_SIGN.equals(action)) { - // DEPRECATED: same as ACTION_CLEARTEXT_SIGN - Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!"); - return signImpl(data, input, output, accSettings, true); - } else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(action)) { - return signImpl(data, input, output, accSettings, false); - } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) { - return encryptAndSignImpl(data, input, output, accSettings, false); - } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) { - return encryptAndSignImpl(data, input, output, accSettings, true); - } else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) { - String currentPkg = getCurrentCallingPackage(); - Set<Long> allowedKeyIds = - mProviderHelper.getAllKeyIdsForApp( - ApiAccounts.buildBaseUri(currentPkg)); - return decryptAndVerifyImpl(data, input, output, allowedKeyIds, false); - } else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) { - String currentPkg = getCurrentCallingPackage(); - Set<Long> allowedKeyIds = - mProviderHelper.getAllKeyIdsForApp( - ApiAccounts.buildBaseUri(currentPkg)); - return decryptAndVerifyImpl(data, input, output, allowedKeyIds, true); - } else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) { - return getKeyImpl(data); - } else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) { - return getKeyIdsImpl(data); - } else { - return null; + String action = data.getAction(); + if (OpenPgpApi.ACTION_CLEARTEXT_SIGN.equals(action)) { + return signImpl(data, input, output, accSettings, true); + } else if (OpenPgpApi.ACTION_SIGN.equals(action)) { + // DEPRECATED: same as ACTION_CLEARTEXT_SIGN + Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!"); + return signImpl(data, input, output, accSettings, true); + } else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(action)) { + return signImpl(data, input, output, accSettings, false); + } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) { + return encryptAndSignImpl(data, input, output, accSettings, false); + } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) { + return encryptAndSignImpl(data, input, output, accSettings, true); + } else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) { + String currentPkg = getCurrentCallingPackage(); + Set<Long> allowedKeyIds = + mProviderHelper.getAllKeyIdsForApp( + ApiAccounts.buildBaseUri(currentPkg)); + return decryptAndVerifyImpl(data, input, output, allowedKeyIds, false); + } else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) { + String currentPkg = getCurrentCallingPackage(); + Set<Long> allowedKeyIds = + mProviderHelper.getAllKeyIdsForApp( + ApiAccounts.buildBaseUri(currentPkg)); + return decryptAndVerifyImpl(data, input, output, allowedKeyIds, true); + } else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) { + return getKeyImpl(data); + } else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) { + return getKeyIdsImpl(data); + } else { + return null; + } + } finally { + // always close input and output file descriptors even in error cases + if (input != null) { + try { + input.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing input ParcelFileDescriptor", e); + } + } + if (output != null) { + try { + output.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing output ParcelFileDescriptor", e); + } + } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java index f4cd553d0..02bf98b12 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java @@ -58,7 +58,6 @@ public class AccountSettingsActivity extends BaseActivity { } }); - mAccountSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById( R.id.api_account_settings_fragment); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java index 36d6ad888..8f822a338 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java @@ -24,6 +24,7 @@ import android.net.Uri; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -37,8 +38,9 @@ import org.sufficientlysecure.keychain.util.Log; public class AppSettingsActivity extends BaseActivity { private Uri mAppUri; - private AppSettingsFragment mSettingsFragment; + private AppSettingsHeaderFragment mSettingsFragment; private AccountsListFragment mAccountsListFragment; + private AppSettingsAllowedKeysListFragment mAllowedKeysFragment; // model AppSettings mAppSettings; @@ -47,7 +49,21 @@ public class AppSettingsActivity extends BaseActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById( + setFullScreenDialogDoneClose(R.string.api_settings_save, + new View.OnClickListener() { + @Override + public void onClick(View v) { + save(); + } + }, + new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + + mSettingsFragment = (AppSettingsHeaderFragment) getSupportFragmentManager().findFragmentById( R.id.api_app_settings_fragment); Intent intent = getIntent(); @@ -62,6 +78,10 @@ public class AppSettingsActivity extends BaseActivity { } } + private void save() { + mAllowedKeysFragment.saveAllowedKeys(); + } + @Override protected void initLayout() { setContentView(R.layout.api_app_settings_activity); @@ -120,10 +140,12 @@ public class AppSettingsActivity extends BaseActivity { Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build(); Log.d(Constants.TAG, "accountsUri: " + accountsUri); - startListFragment(savedInstanceState, accountsUri); + Uri allowedKeysUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ALLOWED_KEYS).build(); + Log.d(Constants.TAG, "allowedKeysUri: " + allowedKeysUri); + startListFragments(savedInstanceState, accountsUri, allowedKeysUri); } - private void startListFragment(Bundle savedInstanceState, Uri dataUri) { + private void startListFragments(Bundle savedInstanceState, Uri accountsUri, Uri allowedKeysUri) { // 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. @@ -132,13 +154,17 @@ public class AppSettingsActivity extends BaseActivity { } // Create an instance of the fragment - mAccountsListFragment = AccountsListFragment.newInstance(dataUri); + mAccountsListFragment = AccountsListFragment.newInstance(accountsUri); + mAllowedKeysFragment = AppSettingsAllowedKeysListFragment.newInstance(allowedKeysUri); // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! getSupportFragmentManager().beginTransaction() .replace(R.id.api_accounts_list_fragment, mAccountsListFragment) .commitAllowingStateLoss(); + getSupportFragmentManager().beginTransaction() + .replace(R.id.api_allowed_keys_list_fragment, mAllowedKeysFragment) + .commitAllowingStateLoss(); // do it immediately! getSupportFragmentManager().executePendingTransactions(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java new file mode 100644 index 000000000..13b242a5e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java @@ -0,0 +1,292 @@ +/* + * 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.remote.ui; + +import android.content.Context; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.os.Bundle; +import android.os.RemoteException; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter; +import org.sufficientlysecure.keychain.ui.widget.FixedListView; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.Vector; + +public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround implements LoaderManager.LoaderCallbacks<Cursor> { + private static final String ARG_DATA_URI = "uri"; + + private SelectKeyCursorAdapter mAdapter; + private Set<Long> mSelectedMasterKeyIds; + private ProviderHelper mProviderHelper; + + private Uri mDataUri; + + /** + * Creates new instance of this fragment + */ + public static AppSettingsAllowedKeysListFragment newInstance(Uri dataUri) { + AppSettingsAllowedKeysListFragment frag = new AppSettingsAllowedKeysListFragment(); + Bundle args = new Bundle(); + + args.putParcelable(ARG_DATA_URI, dataUri); + + frag.setArguments(args); + + return frag; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mProviderHelper = new ProviderHelper(getActivity()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View layout = super.onCreateView(inflater, container, + savedInstanceState); + ListView lv = (ListView) layout.findViewById(android.R.id.list); + ViewGroup parent = (ViewGroup) lv.getParent(); + + /* + * http://stackoverflow.com/a/15880684 + * Remove ListView and add FixedListView in its place. + * This is done here programatically to be still able to use the progressBar of ListFragment. + * + * We want FixedListView to be able to put this ListFragment inside a ScrollView + */ + int lvIndex = parent.indexOfChild(lv); + parent.removeViewAt(lvIndex); + FixedListView newLv = new FixedListView(getActivity()); + newLv.setId(android.R.id.list); + parent.addView(newLv, lvIndex, lv.getLayoutParams()); + return layout; + } + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mDataUri = getArguments().getParcelable(ARG_DATA_URI); + + getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText(getString(R.string.list_empty)); + + mAdapter = new SecretKeyCursorAdapter(getActivity(), null, 0, getListView()); + + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + mSelectedMasterKeyIds = mProviderHelper.getAllKeyIdsForApp(mDataUri); + Log.d(Constants.TAG, "allowed: " + mSelectedMasterKeyIds.toString()); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + /** + * Selects items based on master key ids in list view + * + * @param masterKeyIds + */ + private void preselectMasterKeyIds(Set<Long> masterKeyIds) { + for (int i = 0; i < getListView().getCount(); ++i) { + long listKeyId = mAdapter.getMasterKeyId(i); + for (long keyId : masterKeyIds) { + if (listKeyId == keyId) { + getListView().setItemChecked(i, true); + break; + } + } + } + } + + /** + * Returns all selected master key ids + * + * @return + */ + public Set<Long> getSelectedMasterKeyIds() { + // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key + // ids! + Set<Long> keyIds = new HashSet<>(); + for (int i = 0; i < getListView().getCount(); ++i) { + if (getListView().isItemChecked(i)) { + keyIds.add(mAdapter.getMasterKeyId(i)); + } + } + + return keyIds; + } + + /** + * Returns all selected user ids + * + * @return + */ + public String[] getSelectedUserIds() { + Vector<String> userIds = new Vector<>(); + for (int i = 0; i < getListView().getCount(); ++i) { + if (getListView().isItemChecked(i)) { + userIds.add(mAdapter.getUserId(i)); + } + } + + // make empty array to not return null + String userIdArray[] = new String[0]; + return userIds.toArray(userIdArray); + } + + public void saveAllowedKeys() { + try { + mProviderHelper.saveAllowedKeyIdsForApp(mDataUri, getSelectedMasterKeyIds()); + } catch (RemoteException | OperationApplicationException e) { + Log.e(Constants.TAG, "Problem saving allowed key ids!", e); + } + } + + @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); + + // These are the rows that we will retrieve. + String[] projection = new String[]{ + KeyRings._ID, + KeyRings.MASTER_KEY_ID, + KeyRings.USER_ID, + KeyRings.IS_EXPIRED, + KeyRings.IS_REVOKED, + KeyRings.HAS_ENCRYPT, + KeyRings.VERIFIED, + KeyRings.HAS_ANY_SECRET, + }; + + String inMasterKeyList = null; + if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.size() > 0) { + inMasterKeyList = Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN ("; + Iterator iter = mSelectedMasterKeyIds.iterator(); + while (iter.hasNext()) { + inMasterKeyList += DatabaseUtils.sqlEscapeString("" + iter.next()); + if (iter.hasNext()) { + inMasterKeyList += ", "; + } + } + inMasterKeyList += ")"; + } + + String selection = KeyRings.HAS_ANY_SECRET + " != 0"; + + String orderBy = KeyRings.USER_ID + " ASC"; + if (inMasterKeyList != null) { + // sort by selected master keys + orderBy = inMasterKeyList + " DESC, " + orderBy; + } + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, projection, selection, null, orderBy); + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + + // preselect given master keys + preselectMasterKeyIds(mSelectedMasterKeyIds); + } + + @Override + public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } + + private class SecretKeyCursorAdapter extends SelectKeyCursorAdapter { + + public SecretKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView) { + super(context, c, flags, listView); + } + + @Override + protected void initIndex(Cursor cursor) { + super.initIndex(cursor); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + super.bindView(view, context, cursor); + ViewHolderItem h = (ViewHolderItem) view.getTag(); + + // We care about the checkbox + h.selected.setVisibility(View.VISIBLE); + // the getListView works because this is not a static subclass! + h.selected.setChecked(getListView().isItemChecked(cursor.getPosition())); + + boolean enabled = false; + if ((Boolean) h.statusIcon.getTag()) { + h.statusIcon.setVisibility(View.GONE); + enabled = true; + } + + h.setEnabled(enabled); + } + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java index a6db02708..7beac8973 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java @@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.util.Log; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -public class AppSettingsFragment extends Fragment { +public class AppSettingsHeaderFragment extends Fragment { // model private AppSettings mAppSettings; 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 cbc593b0a..2c5c78161 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 @@ -75,7 +75,7 @@ public class RemoteServiceActivity extends BaseActivity { public static final String EXTRA_ERROR_MESSAGE = "error_message"; // register view - private AppSettingsFragment mAppSettingsFragment; + private AppSettingsHeaderFragment mAppSettingsHeaderFragment; // create acc view private AccountSettingsFragment mAccSettingsFragment; // select pub keys view @@ -115,11 +115,11 @@ public class RemoteServiceActivity extends BaseActivity { setContentView(R.layout.api_remote_register_app); initToolbar(); - mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById( + mAppSettingsHeaderFragment = (AppSettingsHeaderFragment) getSupportFragmentManager().findFragmentById( R.id.api_app_settings_fragment); AppSettings settings = new AppSettings(packageName, packageSignature); - mAppSettingsFragment.setAppSettings(settings); + mAppSettingsHeaderFragment.setAppSettings(settings); // Inflate a "Done"/"Cancel" custom action bar view setFullScreenDialogTwoButtons( @@ -129,7 +129,7 @@ public class RemoteServiceActivity extends BaseActivity { public void onClick(View v) { // Allow - mProviderHelper.insertApiApp(mAppSettingsFragment.getAppSettings()); + mProviderHelper.insertApiApp(mAppSettingsHeaderFragment.getAppSettings()); // give data through for new service call Intent resultData = extras.getParcelable(EXTRA_DATA); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java deleted file mode 100644 index 21377bcd2..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package org.sufficientlysecure.keychain.ui; - -import android.annotation.TargetApi; -import android.content.Intent; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.view.View; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; -import org.sufficientlysecure.keychain.pgp.PgpHelper; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker; - -import java.util.regex.Matcher; - -public class DecryptActivity extends BaseActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - -// activateDrawerNavigation(savedInstanceState); - - View actionFile = findViewById(R.id.decrypt_files); - View actionFromClipboard = findViewById(R.id.decrypt_from_clipboard); - - actionFile.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent filesDecrypt = new Intent(DecryptActivity.this, DecryptFilesActivity.class); - filesDecrypt.setAction(DecryptFilesActivity.ACTION_DECRYPT_DATA_OPEN); - startActivity(filesDecrypt); - } - }); - - actionFromClipboard.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent clipboardDecrypt = new Intent(DecryptActivity.this, DecryptTextActivity.class); - clipboardDecrypt.setAction(DecryptTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD); - startActivityForResult(clipboardDecrypt, 0); - } - }); - } - - @Override - protected void initLayout() { - setContentView(R.layout.decrypt_activity); - } - - @TargetApi(VERSION_CODES.HONEYCOMB) - @Override - protected void onResume() { - super.onResume(); - - // This is an eye candy ice cream sandwich feature, nvm on versions below - if (Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) { - - // get text from clipboard - final CharSequence clipboardText = - ClipboardReflection.getClipboardText(DecryptActivity.this); - - // if it's null, nothing to do here /o/ - if (clipboardText == null) { - return; - } - - new AsyncTask<String, Void, Boolean>() { - @Override - protected Boolean doInBackground(String... clipboardText) { - - // see if it looks like a pgp thing - Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText[0]); - boolean animate = matcher.matches(); - - // see if it looks like another pgp thing - if (!animate) { - matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText[0]); - animate = matcher.matches(); - } - return animate; - } - - @Override - protected void onPostExecute(Boolean animate) { - super.onPostExecute(animate); - - // if so, animate the clipboard icon just a bit~ - if (animate) { - SubtleAttentionSeeker.tada(findViewById(R.id.clipboard_icon), 1.5f).start(); - } - } - }.execute(clipboardText.toString()); - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // if a result has been returned, display a notify - if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { - OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); - result.createNotify(this).show(); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptOverviewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptOverviewFragment.java new file mode 100644 index 000000000..8407a8ca7 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptOverviewFragment.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2014-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.ui; + +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker; + +import java.util.regex.Matcher; + +public class DecryptOverviewFragment extends Fragment { + + View mActionFile; + View mActionFromClipboard; + View mClipboardIcon; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.decrypt_overview_fragment, container, false); + + mActionFile = view.findViewById(R.id.decrypt_files); + mActionFromClipboard = view.findViewById(R.id.decrypt_from_clipboard); + mClipboardIcon = view.findViewById(R.id.clipboard_icon); + + mActionFile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent filesDecrypt = new Intent(getActivity(), DecryptFilesActivity.class); + filesDecrypt.setAction(DecryptFilesActivity.ACTION_DECRYPT_DATA_OPEN); + startActivity(filesDecrypt); + } + }); + + mActionFromClipboard.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent clipboardDecrypt = new Intent(getActivity(), DecryptTextActivity.class); + clipboardDecrypt.setAction(DecryptTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD); + startActivityForResult(clipboardDecrypt, 0); + } + }); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + + // get text from clipboard + final CharSequence clipboardText = + ClipboardReflection.getClipboardText(getActivity()); + + // if it's null, nothing to do here /o/ + if (clipboardText == null) { + return; + } + + new AsyncTask<String, Void, Boolean>() { + @Override + protected Boolean doInBackground(String... clipboardText) { + + // see if it looks like a pgp thing + Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText[0]); + boolean animate = matcher.matches(); + + // see if it looks like another pgp thing + if (!animate) { + matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText[0]); + animate = matcher.matches(); + } + return animate; + } + + @Override + protected void onPostExecute(Boolean animate) { + super.onPostExecute(animate); + + // if so, animate the clipboard icon just a bit~ + if (animate) { + SubtleAttentionSeeker.tada(mClipboardIcon, 1.5f).start(); + } + } + }.execute(clipboardText.toString()); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + // if a result has been returned, display a notify + if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { + OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); + result.createNotify(getActivity()).show(); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java deleted file mode 100644 index 712516b8d..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java +++ /dev/null @@ -1,305 +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.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.res.Configuration; -import android.graphics.Color; -import android.os.Bundle; -import android.support.v4.app.ActionBarDrawerToggle; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v4.widget.FixedDrawerLayout; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; - -public abstract class DrawerActivity extends BaseActivity { - private FixedDrawerLayout mDrawerLayout; - private ListView mDrawerList; - private ActionBarDrawerToggle mDrawerToggle; - - private CharSequence mDrawerTitle; - private CharSequence mTitle; - private boolean mIsDrawerLocked = false; - - private Class mSelectedItem; - - private static final int MENU_ID_PREFERENCE = 222; - private static final int MENU_ID_HELP = 223; - - protected void deactivateDrawerNavigation() { - ((DrawerLayout) findViewById(R.id.drawer_layout)).setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - } - - protected void activateDrawerNavigation(Bundle savedInstanceState) { - mDrawerTitle = getString(R.string.app_name); - mDrawerLayout = (FixedDrawerLayout) findViewById(R.id.drawer_layout); - mDrawerList = (ListView) findViewById(R.id.left_drawer); - ViewGroup viewGroup = (ViewGroup) findViewById(R.id.content_frame); - int leftMarginLoaded = ((ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams()).leftMargin; - int leftMarginInTablets = (int) getResources().getDimension(R.dimen.drawer_size); - int errorInMarginAllowed = 5; - - // if the left margin of the loaded layout is close to the - // one used in tablets then set drawer as open and locked - if (Math.abs(leftMarginLoaded - leftMarginInTablets) < errorInMarginAllowed) { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, mDrawerList); - mDrawerLayout.setScrimColor(Color.TRANSPARENT); - mIsDrawerLocked = true; - } else { - // set a custom shadow that overlays the main content when the drawer opens - mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); - mIsDrawerLocked = false; - } - - NavItem mItemIconTexts[] = new NavItem[]{ - new NavItem(R.drawable.ic_action_accounts, getString(R.string.nav_keys)), - new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_text)), - new NavItem(R.drawable.ic_action_secure, getString(R.string.nav_encrypt_files)), - new NavItem(R.drawable.ic_action_not_secure, getString(R.string.nav_decrypt)), - new NavItem(R.drawable.ic_action_view_as_list, getString(R.string.nav_apps))}; - - mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item, - mItemIconTexts)); - - mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); - - // enable ActionBar app icon to behave as action to toggle nav drawer - // if the drawer is not locked - if (!mIsDrawerLocked) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - } - - // ActionBarDrawerToggle ties together the the proper interactions - // between the sliding drawer and the action bar app icon - mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */ - mDrawerLayout, /* DrawerLayout object */ - R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */ - R.string.drawer_open, /* "open drawer" description for accessibility */ - R.string.drawer_close /* "close drawer" description for accessibility */ - ) { - public void onDrawerClosed(View view) { - getSupportActionBar().setTitle(mTitle); - - callIntentForDrawerItem(mSelectedItem); - } - - public void onDrawerOpened(View drawerView) { - mTitle = getSupportActionBar().getTitle(); - getSupportActionBar().setTitle(mDrawerTitle); - // creates call to onPrepareOptionsMenu() - supportInvalidateOptionsMenu(); - } - }; - - if (!mIsDrawerLocked) { - mDrawerLayout.setDrawerListener(mDrawerToggle); - } else { - // If the drawer is locked open make it un-focusable - // so that it doesn't consume all the Back button presses - mDrawerLayout.setFocusableInTouchMode(false); - } - } - - /** - * Uses startActivity to call the Intent of the given class - * - * @param drawerItem the class of the drawer item you want to load. Based on Constants.DrawerItems.* - */ - public void callIntentForDrawerItem(Class drawerItem) { - // creates call to onPrepareOptionsMenu() - supportInvalidateOptionsMenu(); - - // call intent activity if selected - if (drawerItem != null) { - finish(); - overridePendingTransition(0, 0); - - Intent intent = new Intent(this, drawerItem); - startActivity(intent); - - // disable animation of activity start - overridePendingTransition(0, 0); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (mDrawerToggle == null) { - return super.onCreateOptionsMenu(menu); - } - - menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences); - menu.add(42, MENU_ID_HELP, 101, R.string.menu_help); - - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (mDrawerToggle == null) { - return super.onOptionsItemSelected(item); - } - - // The action bar home/up action should open or close the drawer. - // ActionBarDrawerToggle will take care of this. - if (mDrawerToggle.onOptionsItemSelected(item)) { - return true; - } - - switch (item.getItemId()) { - case MENU_ID_PREFERENCE: { - Intent intent = new Intent(this, SettingsActivity.class); - startActivity(intent); - return true; - } - case MENU_ID_HELP: { - Intent intent = new Intent(this, HelpActivity.class); - startActivity(intent); - return true; - } - default: - return super.onOptionsItemSelected(item); - } - } - - /** - * The click listener for ListView in the navigation drawer - */ - private class DrawerItemClickListener implements ListView.OnItemClickListener { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - selectItem(position); - } - } - - private void selectItem(int position) { - // update selected item and title, then close the drawer - mDrawerList.setItemChecked(position, true); - // set selected class - mSelectedItem = Constants.DrawerItems.ARRAY[position]; - - // setTitle(mDrawerTitles[position]); - // If drawer isn't locked just close the drawer and - // it will move to the selected item by itself (via drawer toggle listener) - if (!mIsDrawerLocked) { - mDrawerLayout.closeDrawer(mDrawerList); - // else move to the selected item yourself - } else { - callIntentForDrawerItem(mSelectedItem); - } - } - - /** - * When using the ActionBarDrawerToggle, you must call it during onPostCreate() and - * onConfigurationChanged()... - */ - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - // Sync the toggle state after onRestoreInstanceState has occurred. - if (mDrawerToggle != null) { - mDrawerToggle.syncState(); - } - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - // Pass any configuration change to the drawer toggles - if (mDrawerToggle != null) { - mDrawerToggle.onConfigurationChanged(newConfig); - } - } - - private class NavItem { - public int icon; // res-id - public String title; - - /** - * NavItem constructor - * - * @param icon The icons resource-id - * @param title The title of the menu entry - */ - public NavItem(int icon, String title) { - super(); - this.icon = icon; - this.title = title; - } - } - - private class NavigationDrawerAdapter extends ArrayAdapter<NavItem> { - Context mContext; - int mLayoutResourceId; - NavItem mData[] = null; - - public NavigationDrawerAdapter(Context context, int layoutResourceId, NavItem[] data) { - super(context, layoutResourceId, data); - this.mLayoutResourceId = layoutResourceId; - this.mContext = context; - this.mData = data; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - NavItemHolder holder; - - if (row == null) { - LayoutInflater inflater = ((Activity) mContext).getLayoutInflater(); - row = inflater.inflate(mLayoutResourceId, parent, false); - - holder = new NavItemHolder(); - holder.mImg = (ImageView) row.findViewById(R.id.drawer_item_icon); - holder.mTxtTitle = (TextView) row.findViewById(R.id.drawer_item_text); - - row.setTag(holder); - } else { - holder = (NavItemHolder) row.getTag(); - } - - NavItem item = mData[position]; - holder.mTxtTitle.setText(item.title); - holder.mImg.setImageResource(item.icon); - - return row; - } - - } - - static class NavItemHolder { - ImageView mImg; - TextView mTxtTitle; - } - -} 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 4e5e8c631..8abb381f8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -110,7 +110,6 @@ public class KeyListFragment extends LoaderFragment super.onCreate(savedInstanceState); mExportHelper = new ExportHelper(getActivity()); - } /** @@ -205,84 +204,82 @@ public class KeyListFragment extends LoaderFragment mStickyList.setFastScrollEnabled(true); /* - * Multi-selection is only available for Android >= 3.0 + * Multi-selection */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mStickyList.setFastScrollAlwaysVisible(true); + mStickyList.setFastScrollAlwaysVisible(true); - mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); - mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() { + mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); + mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() { - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - android.view.MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.key_list_multi, menu); - mActionMode = mode; - return true; - } + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + android.view.MenuInflater inflater = getActivity().getMenuInflater(); + inflater.inflate(R.menu.key_list_multi, menu); + mActionMode = mode; + return true; + } - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - // get IDs for checked positions as long array - long[] ids; + // get IDs for checked positions as long array + long[] ids; - switch (item.getItemId()) { - case R.id.menu_key_list_multi_encrypt: { - ids = mAdapter.getCurrentSelectedMasterKeyIds(); - encrypt(mode, ids); - break; - } - case R.id.menu_key_list_multi_delete: { - ids = mAdapter.getCurrentSelectedMasterKeyIds(); - showDeleteKeyDialog(mode, ids, mAdapter.isAnySecretSelected()); - break; - } - case R.id.menu_key_list_multi_export: { - ids = mAdapter.getCurrentSelectedMasterKeyIds(); - ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity()); - mExportHelper.showExportKeysDialog(ids, Constants.Path.APP_DIR_FILE, - mAdapter.isAnySecretSelected()); - break; - } - case R.id.menu_key_list_multi_select_all: { - // select all - for (int i = 0; i < mStickyList.getCount(); i++) { - mStickyList.setItemChecked(i, true); - } - break; + switch (item.getItemId()) { + case R.id.menu_key_list_multi_encrypt: { + ids = mAdapter.getCurrentSelectedMasterKeyIds(); + encrypt(mode, ids); + break; + } + case R.id.menu_key_list_multi_delete: { + ids = mAdapter.getCurrentSelectedMasterKeyIds(); + showDeleteKeyDialog(mode, ids, mAdapter.isAnySecretSelected()); + break; + } + case R.id.menu_key_list_multi_export: { + ids = mAdapter.getCurrentSelectedMasterKeyIds(); + ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity()); + mExportHelper.showExportKeysDialog(ids, Constants.Path.APP_DIR_FILE, + mAdapter.isAnySecretSelected()); + break; + } + case R.id.menu_key_list_multi_select_all: { + // select all + for (int i = 0; i < mStickyList.getCount(); i++) { + mStickyList.setItemChecked(i, true); } + break; } - return true; } + return true; + } - @Override - public void onDestroyActionMode(ActionMode mode) { - mActionMode = null; - mAdapter.clearSelection(); - } + @Override + public void onDestroyActionMode(ActionMode mode) { + mActionMode = null; + mAdapter.clearSelection(); + } - @Override - public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { - if (checked) { - mAdapter.setNewSelection(position, checked); - } else { - mAdapter.removeSelection(position); - } - int count = mStickyList.getCheckedItemCount(); - String keysSelected = getResources().getQuantityString( - R.plurals.key_list_selected_keys, count, count); - mode.setTitle(keysSelected); + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked) { + if (checked) { + mAdapter.setNewSelection(position, checked); + } else { + mAdapter.removeSelection(position); } + int count = mStickyList.getCheckedItemCount(); + String keysSelected = getResources().getQuantityString( + R.plurals.key_list_selected_keys, count, count); + mode.setTitle(keysSelected); + } - }); - } + }); // We have a menu item to show in action bar. setHasOptionsMenu(true); @@ -369,9 +366,7 @@ public class KeyListFragment extends LoaderFragment // end action mode, if any if (mActionMode != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mActionMode.finish(); - } + mActionMode.finish(); } // The list should now be shown. @@ -401,7 +396,6 @@ public class KeyListFragment extends LoaderFragment startActivity(viewIntent); } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) protected void encrypt(ActionMode mode, long[] masterKeyIds) { Intent intent = new Intent(getActivity(), EncryptFilesActivity.class); intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); @@ -418,7 +412,6 @@ public class KeyListFragment extends LoaderFragment * @param masterKeyIds * @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) { // Can only work on singular secret keys if (hasSecret && masterKeyIds.length > 1) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java index 57910af5e..bc9c36a2e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java @@ -38,15 +38,14 @@ public abstract class NavDrawerActivity extends MaterialNavigationDrawer { // set the header image // create and set the header - View view = LayoutInflater.from(this).inflate(R.layout.custom_drawer, null); - setDrawerHeaderCustom(view); + setDrawerHeaderImage(R.drawable.drawer_header); // create sections addSection(newSection(getString(R.string.title_keys), R.drawable.ic_vpn_key_black_24dp, new KeyListFragment())); addSection(newSection(getString(R.string.title_encrypt_text), R.drawable.ic_lock_outline_black_24dp, new Intent(this, EncryptTextActivity.class))); addSection(newSection(getString(R.string.title_encrypt_files), R.drawable.ic_lock_outline_black_24dp, new Intent(this, EncryptFilesActivity.class))); - addSection(newSection(getString(R.string.title_decrypt), R.drawable.ic_lock_open_black_24dp, new Intent(this, DecryptActivity.class))); + addSection(newSection(getString(R.string.title_decrypt), R.drawable.ic_lock_open_black_24dp, new DecryptOverviewFragment())); addSection(newSection(getString(R.string.title_api_registered_apps), R.drawable.ic_apps_black_24dp, new AppsListFragment())); // create bottom section diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java deleted file mode 100644 index 0e3374833..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org> - * - * 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.View; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; - -public class SelectPublicKeyActivity extends BaseActivity { - - // Actions for internal use only: - public static final String ACTION_SELECT_PUBLIC_KEYS = Constants.INTENT_PREFIX - + "SELECT_PUBLIC_KEYRINGS"; - - public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids"; - - public static final String RESULT_EXTRA_MASTER_KEY_IDS = "master_key_ids"; - public static final String RESULT_EXTRA_USER_IDS = "user_ids"; - - SelectPublicKeyFragment mSelectFragment; - - long mSelectedMasterKeyIds[]; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Inflate a "Done"/"Cancel" custom action bar view - setFullScreenDialogDoneClose(R.string.btn_okay, - new View.OnClickListener() { - @Override - public void onClick(View v) { - okClicked(); - } - }, - new View.OnClickListener() { - @Override - public void onClick(View v) { - cancelClicked(); - } - }); - - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - - handleIntent(getIntent()); - - // Check that the activity is using the layout version with - // the fragment_container FrameLayout - if (findViewById(R.id.select_public_key_fragment_container) != null) { - - // 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; - } - - // Create an instance of the fragment - mSelectFragment = SelectPublicKeyFragment.newInstance(mSelectedMasterKeyIds); - - // Add the fragment to the 'fragment_container' FrameLayout - getSupportFragmentManager().beginTransaction() - .add(R.id.select_public_key_fragment_container, mSelectFragment).commit(); - } - - // TODO: reimplement! - // mFilterLayout = findViewById(R.id.layout_filter); - // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo); - // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear); - // - // mClearFilterButton.setOnClickListener(new OnClickListener() { - // public void onClick(View v) { - // handleIntent(new Intent()); - // } - // }); - - } - - @Override - protected void initLayout() { - setContentView(R.layout.select_public_key_activity); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - handleIntent(intent); - } - - private void handleIntent(Intent intent) { - // TODO: reimplement search! - - // String searchString = null; - // if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - // searchString = intent.getStringExtra(SearchManager.QUERY); - // if (searchString != null && searchString.trim().length() == 0) { - // searchString = null; - // } - // } - - // if (searchString == null) { - // mFilterLayout.setVisibility(View.GONE); - // } else { - // mFilterLayout.setVisibility(View.VISIBLE); - // mFilterInfo.setText(getString(R.string.filterInfo, searchString)); - // } - - // preselected master keys - mSelectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); - } - - private void cancelClicked() { - setResult(RESULT_CANCELED, null); - finish(); - } - - private void okClicked() { - Intent data = new Intent(); - data.putExtra(RESULT_EXTRA_MASTER_KEY_IDS, mSelectFragment.getSelectedMasterKeyIds()); - data.putExtra(RESULT_EXTRA_USER_IDS, mSelectFragment.getSelectedUserIds()); - setResult(RESULT_OK, data); - finish(); - } - -} |