aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-01-31 18:49:54 +0100
committerVincent Breitmoser <valodim@mugenguild.com>2015-01-31 18:49:54 +0100
commit0b6dc65c97b0fb5dae9bf1a06f9c7db0b65ea4ad (patch)
tree92954cab7f0aa299c2658a9f15586168ccf12e4f /OpenKeychain/src/main/java
parent5466d1e9802815726d713bca2aeabaff2861519c (diff)
parente651a392795caa395b060946b0cfaca5a5b41ded (diff)
downloadopen-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')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java52
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java620
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java36
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java292
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java)2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java128
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptOverviewFragment.java130
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java305
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java133
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java144
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();
- }
-
-}