diff options
| author | Vincent Breitmoser <valodim@mugenguild.com> | 2015-01-31 18:49:54 +0100 | 
|---|---|---|
| committer | Vincent Breitmoser <valodim@mugenguild.com> | 2015-01-31 18:49:54 +0100 | 
| commit | 0b6dc65c97b0fb5dae9bf1a06f9c7db0b65ea4ad (patch) | |
| tree | 92954cab7f0aa299c2658a9f15586168ccf12e4f /OpenKeychain | |
| parent | 5466d1e9802815726d713bca2aeabaff2861519c (diff) | |
| parent | e651a392795caa395b060946b0cfaca5a5b41ded (diff) | |
| download | open-keychain-0b6dc65c97b0fb5dae9bf1a06f9c7db0b65ea4ad.tar.gz open-keychain-0b6dc65c97b0fb5dae9bf1a06f9c7db0b65ea4ad.tar.bz2 open-keychain-0b6dc65c97b0fb5dae9bf1a06f9c7db0b65ea4ad.zip | |
Merge remote-tracking branch 'origin/development' into development
Diffstat (limited to 'OpenKeychain')
44 files changed, 1181 insertions, 1369 deletions
| diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index 3bcfdda5e..eca62026a 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -32,7 +32,7 @@ dependencies {  android {      compileSdkVersion 21 -    buildToolsVersion '21.1.1' +    buildToolsVersion '21.1.2'      defaultConfig {          minSdkVersion 15 diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 865e0272d..d47729127 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -86,11 +86,6 @@              </intent-filter>          </activity>          <activity -            android:name=".ui.KeyListActivity" -            android:configChanges="orientation|screenSize|keyboardHidden|keyboard" -            android:label="@string/app_name" -            android:windowSoftInputMode="stateAlwaysHidden" /> -        <activity              android:name=".ui.FirstTimeActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:label="@string/app_name" @@ -100,10 +95,10 @@              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:windowSoftInputMode="stateHidden"              android:label="@string/title_create_key" -            android:parentActivityName=".ui.KeyListActivity"> +            android:parentActivityName=".ui.MainActivity">              <meta-data                  android:name="android.support.PARENT_ACTIVITY" -                android:value=".ui.KeyListActivity" /> +                android:value=".ui.MainActivity" />          </activity>          <activity              android:name=".ui.EditKeyActivity" @@ -117,10 +112,10 @@              android:name=".ui.ViewKeyActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:label="@string/title_key_details" -            android:parentActivityName=".ui.KeyListActivity"> +            android:parentActivityName=".ui.MainActivity">              <meta-data                  android:name="android.support.PARENT_ACTIVITY" -                android:value=".ui.KeyListActivity" /> +                android:value=".ui.MainActivity" />              <intent-filter>                  <action android:name="android.intent.action.VIEW" />                  <category android:name="android.intent.category.DEFAULT" /> @@ -141,17 +136,12 @@              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:label="@string/title_exchange_keys"              android:windowSoftInputMode="stateHidden" -            android:parentActivityName=".ui.KeyListActivity"> +            android:parentActivityName=".ui.MainActivity">              <meta-data                  android:name="android.support.PARENT_ACTIVITY" -                android:value=".ui.KeyListActivity" /> +                android:value=".ui.MainActivity" />          </activity>          <activity -            android:name=".ui.SelectPublicKeyActivity" -            android:configChanges="orientation|screenSize|keyboardHidden|keyboard" -            android:label="@string/title_select_recipients" -            android:launchMode="singleTop" /> -        <activity              android:name=".ui.EncryptFilesActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:label="@string/title_encrypt_files" @@ -204,19 +194,14 @@              </intent-filter>          </activity>          <activity -            android:name=".ui.DecryptActivity" -            android:configChanges="orientation|screenSize|keyboardHidden|keyboard" -            android:label="@string/title_decrypt" -            android:windowSoftInputMode="stateHidden" /> -        <activity              android:name=".ui.DecryptTextActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:label="@string/title_decrypt"              android:windowSoftInputMode="stateHidden" -            android:parentActivityName=".ui.DecryptActivity"> +            android:parentActivityName=".ui.MainActivity">              <meta-data                  android:name="android.support.PARENT_ACTIVITY" -                android:value=".ui.DecryptActivity" /> +                android:value=".ui.MainActivity" />              <!-- Keychain's own Actions -->              <!-- DECRYPT with text as extra --> @@ -240,10 +225,10 @@              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:label="@string/title_decrypt"              android:windowSoftInputMode="stateHidden" -            android:parentActivityName=".ui.DecryptActivity"> +            android:parentActivityName=".ui.MainActivity">              <meta-data                  android:name="android.support.PARENT_ACTIVITY" -                android:value=".ui.DecryptActivity" /> +                android:value=".ui.MainActivity" />              <!-- VIEW with mimeType application/octet-stream, application/pgp and text/pgp  -->              <intent-filter android:label="@string/intent_send_decrypt"> @@ -446,7 +431,7 @@              android:label="@string/title_certify_key">              <meta-data                  android:name="android.support.PARENT_ACTIVITY" -                android:value=".ui.KeyListActivity" /> +                android:value=".ui.MainActivity" />          </activity>          <activity              android:name=".ui.QrCodeScanActivity" @@ -653,7 +638,7 @@              </intent-filter>              <meta-data                  android:name="android.support.PARENT_ACTIVITY" -                android:value=".ui.KeyListActivity" /> +                android:value=".ui.MainActivity" />          </activity>          <activity              android:name=".ui.ConsolidateDialogActivity" @@ -719,18 +704,13 @@              android:launchMode="singleTop"              android:process=":remote_api" />          <activity -            android:name=".remote.ui.AppsListActivity" -            android:configChanges="orientation|screenSize|keyboardHidden|keyboard" -            android:exported="false" -            android:label="@string/title_api_registered_apps" /> -        <activity              android:name=".remote.ui.AppSettingsActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:exported="false">              <meta-data                  android:name="android.support.PARENT_ACTIVITY" -                android:value=".remote.ui.AppsListActivity" /> +                android:value=".ui.MainActivity" />          </activity>          <activity              android:name=".remote.ui.AccountSettingsActivity" 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(); -    } - -} diff --git a/OpenKeychain/src/main/res/drawable/drawer_header.png b/OpenKeychain/src/main/res/drawable/drawer_header.pngBinary files differ new file mode 100644 index 000000000..614098bd7 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable/drawer_header.png diff --git a/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml b/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml deleted file mode 100644 index 06487a982..000000000 --- a/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:orientation="horizontal" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <android.support.v4.widget.FixedDrawerLayout -        android:id="@+id/drawer_layout" -        android:layout_width="match_parent" -        android:layout_height="match_parent"> - -        <include layout="@layout/drawer_list" /> - -    </android.support.v4.widget.FixedDrawerLayout> - -    <include layout="@layout/decrypt_content" /> - -</FrameLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout-large/encrypt_files_activity.xml b/OpenKeychain/src/main/res/layout-large/encrypt_files_activity.xml deleted file mode 100644 index 4b277c331..000000000 --- a/OpenKeychain/src/main/res/layout-large/encrypt_files_activity.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:orientation="horizontal" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <android.support.v4.widget.FixedDrawerLayout -        android:id="@+id/drawer_layout" -        android:layout_width="match_parent" -        android:layout_height="match_parent"> - -        <include layout="@layout/drawer_list" /> - -    </android.support.v4.widget.FixedDrawerLayout> - -    <include layout="@layout/encrypt_files_content" /> -</FrameLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout-large/encrypt_text_activity.xml b/OpenKeychain/src/main/res/layout-large/encrypt_text_activity.xml deleted file mode 100644 index 5cf838c46..000000000 --- a/OpenKeychain/src/main/res/layout-large/encrypt_text_activity.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:orientation="horizontal" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <android.support.v4.widget.FixedDrawerLayout -        android:id="@+id/drawer_layout" -        android:layout_width="match_parent" -        android:layout_height="match_parent"> - -        <include layout="@layout/drawer_list" /> - -    </android.support.v4.widget.FixedDrawerLayout> - -    <include layout="@layout/encrypt_text_content" /> -</FrameLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout-large/key_list_activity.xml b/OpenKeychain/src/main/res/layout-large/key_list_activity.xml deleted file mode 100644 index 039081cd7..000000000 --- a/OpenKeychain/src/main/res/layout-large/key_list_activity.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:orientation="horizontal" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <android.support.v4.widget.FixedDrawerLayout -        android:id="@+id/drawer_layout" -        android:layout_width="match_parent" -        android:layout_height="match_parent"> - -        <include layout="@layout/drawer_list" /> - -    </android.support.v4.widget.FixedDrawerLayout> - -    <include layout="@layout/key_list_content" /> - -</FrameLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml index 800e2eb87..3da0d4e7d 100644 --- a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml +++ b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml @@ -28,7 +28,7 @@                  <fragment                      android:id="@+id/api_app_settings_fragment" -                    android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsFragment" +                    android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsHeaderFragment"                      android:layout_width="match_parent"                      android:layout_height="wrap_content"                      tools:layout="@layout/api_app_settings_fragment" /> @@ -37,6 +37,18 @@                      style="@style/SectionHeader"                      android:layout_width="match_parent"                      android:layout_height="wrap_content" +                    android:text="@string/api_settings_allowed_keys" /> + +                <FrameLayout +                    android:id="@+id/api_allowed_keys_list_fragment" +                    android:layout_width="match_parent" +                    android:layout_height="match_parent" +                    android:orientation="vertical" /> + +                <TextView +                    style="@style/SectionHeader" +                    android:layout_width="match_parent" +                    android:layout_height="wrap_content"                      android:text="@string/api_settings_accounts" />                  <FrameLayout diff --git a/OpenKeychain/src/main/res/layout/api_apps_adapter_list_item.xml b/OpenKeychain/src/main/res/layout/api_apps_adapter_list_item.xml index b67da13a6..c4ea46d43 100644 --- a/OpenKeychain/src/main/res/layout/api_apps_adapter_list_item.xml +++ b/OpenKeychain/src/main/res/layout/api_apps_adapter_list_item.xml @@ -13,16 +13,6 @@          android:layout_centerVertical="true"          android:src="@drawable/ic_launcher" /> -    <TextView -        android:id="@+id/api_apps_adapter_item_name" -        android:layout_width="wrap_content" -        android:layout_height="wrap_content" -        android:layout_marginLeft="8dp" -        android:text="Application Name" -        android:textAppearance="?android:attr/textAppearanceMedium" -        android:layout_centerVertical="true" -        android:layout_toRightOf="@+id/api_apps_adapter_item_icon" /> -      <ImageView          android:id="@+id/api_apps_adapter_install_icon"          android:layout_width="wrap_content" @@ -33,5 +23,15 @@          android:layout_alignParentEnd="true"          android:padding="8dp" /> +    <TextView +        android:id="@+id/api_apps_adapter_item_name" +        android:layout_width="wrap_content" +        android:layout_height="wrap_content" +        android:layout_marginLeft="8dp" +        android:text="Application Name" +        android:textAppearance="?android:attr/textAppearanceMedium" +        android:layout_centerVertical="true" +        android:layout_toLeftOf="@+id/api_apps_adapter_install_icon" +        android:layout_toRightOf="@+id/api_apps_adapter_item_icon" />  </RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/api_remote_register_app.xml b/OpenKeychain/src/main/res/layout/api_remote_register_app.xml index 19981f59e..300fde420 100644 --- a/OpenKeychain/src/main/res/layout/api_remote_register_app.xml +++ b/OpenKeychain/src/main/res/layout/api_remote_register_app.xml @@ -30,7 +30,7 @@              <fragment                  android:id="@+id/api_app_settings_fragment" -                android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsFragment" +                android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsHeaderFragment"                  android:layout_width="match_parent"                  android:layout_height="wrap_content"                  tools:layout="@layout/api_app_settings_fragment" /> diff --git a/OpenKeychain/src/main/res/layout/decrypt_activity.xml b/OpenKeychain/src/main/res/layout/decrypt_activity.xml deleted file mode 100644 index bb0e463b3..000000000 --- a/OpenKeychain/src/main/res/layout/decrypt_activity.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<android.support.v4.widget.FixedDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:id="@+id/drawer_layout" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <include layout="@layout/decrypt_content"/> - -    <include layout="@layout/drawer_list" /> - -</android.support.v4.widget.FixedDrawerLayout> diff --git a/OpenKeychain/src/main/res/layout/decrypt_content.xml b/OpenKeychain/src/main/res/layout/decrypt_content.xml deleted file mode 100644 index 8944c821f..000000000 --- a/OpenKeychain/src/main/res/layout/decrypt_content.xml +++ /dev/null @@ -1,117 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <include -        android:id="@+id/toolbar" -        layout="@layout/toolbar" /> - -    <LinearLayout -        android:layout_below="@id/toolbar" -        android:id="@+id/content_frame" -        android:layout_marginLeft="@dimen/drawer_content_padding" -        android:layout_width="match_parent" -        android:layout_height="match_parent" -        android:orientation="vertical"> - -        <include layout="@layout/notify_area" /> - -        <LinearLayout -            android:layout_width="match_parent" -            android:layout_height="match_parent" -            android:paddingTop="4dp" -            android:paddingLeft="16dp" -            android:paddingRight="16dp" -            android:orientation="vertical"> - -            <TextView -                style="@style/SectionHeader" -                android:layout_width="match_parent" -                android:layout_height="wrap_content" -                android:layout_marginTop="16dp" -                android:text="@string/section_decrypt_files" /> - - -            <TextView -                android:id="@+id/decrypt_files" -                android:paddingLeft="8dp" -                android:paddingRight="8dp" -                android:textAppearance="?android:attr/textAppearanceMedium" -                android:layout_width="match_parent" -                android:layout_height="wrap_content" -                android:minHeight="?android:attr/listPreferredItemHeight" -                android:clickable="true" -                style="@style/SelectableItem" -                android:text="@string/btn_decrypt_files" -                android:drawableRight="@drawable/ic_action_collection" -                android:drawablePadding="8dp" -                android:gravity="center_vertical" /> - -            <View -                android:layout_width="match_parent" -                android:layout_height="1dip" -                android:background="?android:attr/listDivider" -                android:layout_marginBottom="8dp" /> - -            <TextView -                style="@style/SectionHeader" -                android:layout_width="match_parent" -                android:layout_height="wrap_content" -                android:layout_marginTop="32dp" -                android:text="@string/section_decrypt_text" /> - -            <LinearLayout -                android:id="@+id/decrypt_from_clipboard" -                android:layout_width="match_parent" -                android:layout_height="?android:attr/listPreferredItemHeight" -                android:clickable="true" -                android:paddingRight="4dp" -                style="@style/SelectableItem" -                android:orientation="horizontal"> - -                <LinearLayout -                    android:layout_width="0dp" -                    android:layout_height="match_parent" -                    android:layout_weight="1" -                    android:paddingRight="4dp" -                    android:gravity="center_vertical" -                    android:orientation="vertical"> - -                    <TextView -                        android:paddingLeft="8dp" -                        android:textAppearance="?android:attr/textAppearanceMedium" -                        android:layout_width="match_parent" -                        android:layout_height="wrap_content" -                        android:text="@string/btn_decrypt_clipboard" /> - -                    <TextView -                        android:paddingLeft="8dp" -                        android:textAppearance="?android:attr/textAppearanceSmall" -                        android:layout_width="match_parent" -                        android:layout_height="wrap_content" -                        android:textColor="@color/tertiary_text_light" -                        android:text="@string/btn_decrypt_and_verify" -                        android:gravity="center_vertical" /> - -                </LinearLayout> - -                <ImageView -                    android:id="@+id/clipboard_icon" -                    android:layout_width="wrap_content" -                    android:layout_height="match_parent" -                    android:padding="8dp" -                    android:src="@drawable/ic_action_paste" -                    android:layout_gravity="center_vertical" /> - -            </LinearLayout> - -            <View -                android:layout_width="match_parent" -                android:layout_height="1dip" -                android:background="?android:attr/listDivider" -                android:layout_marginBottom="8dp" /> - -        </LinearLayout> -    </LinearLayout> -</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/decrypt_overview_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_overview_fragment.xml new file mode 100644 index 000000000..c3e31bf11 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/decrypt_overview_fragment.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:paddingTop="4dp" +    android:paddingLeft="16dp" +    android:paddingRight="16dp" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    android:orientation="vertical"> + +    <TextView +        style="@style/SectionHeader" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:layout_marginTop="16dp" +        android:text="@string/section_decrypt_files" /> + +    <TextView +        android:id="@+id/decrypt_files" +        android:paddingLeft="8dp" +        android:paddingRight="8dp" +        android:textAppearance="?android:attr/textAppearanceMedium" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:minHeight="?android:attr/listPreferredItemHeight" +        android:clickable="true" +        style="@style/SelectableItem" +        android:text="@string/btn_decrypt_files" +        android:drawableRight="@drawable/ic_action_collection" +        android:drawablePadding="8dp" +        android:gravity="center_vertical" /> + +    <View +        android:layout_width="match_parent" +        android:layout_height="1dip" +        android:background="?android:attr/listDivider" +        android:layout_marginBottom="8dp" /> + +    <TextView +        style="@style/SectionHeader" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:layout_marginTop="32dp" +        android:text="@string/section_decrypt_text" /> + +    <LinearLayout +        android:id="@+id/decrypt_from_clipboard" +        android:layout_width="match_parent" +        android:layout_height="?android:attr/listPreferredItemHeight" +        android:clickable="true" +        android:paddingRight="4dp" +        style="@style/SelectableItem" +        android:orientation="horizontal"> + +        <LinearLayout +            android:layout_width="0dp" +            android:layout_height="match_parent" +            android:layout_weight="1" +            android:paddingRight="4dp" +            android:gravity="center_vertical" +            android:orientation="vertical"> + +            <TextView +                android:paddingLeft="8dp" +                android:textAppearance="?android:attr/textAppearanceMedium" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:text="@string/btn_decrypt_clipboard" /> + +            <TextView +                android:paddingLeft="8dp" +                android:textAppearance="?android:attr/textAppearanceSmall" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:textColor="@color/tertiary_text_light" +                android:text="@string/btn_decrypt_and_verify" +                android:gravity="center_vertical" /> + +        </LinearLayout> + +        <ImageView +            android:id="@+id/clipboard_icon" +            android:layout_width="wrap_content" +            android:layout_height="match_parent" +            android:padding="8dp" +            android:src="@drawable/ic_action_paste" +            android:layout_gravity="center_vertical" /> + +    </LinearLayout> + +    <View +        android:layout_width="match_parent" +        android:layout_height="1dip" +        android:background="?android:attr/listDivider" +        android:layout_marginBottom="8dp" /> + +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/custom_drawer.xml b/OpenKeychain/src/main/res/layout/drawer_custom_header.xml index 021226ba3..021226ba3 100644 --- a/OpenKeychain/src/main/res/layout/custom_drawer.xml +++ b/OpenKeychain/src/main/res/layout/drawer_custom_header.xml diff --git a/OpenKeychain/src/main/res/layout/drawer_list.xml b/OpenKeychain/src/main/res/layout/drawer_list.xml deleted file mode 100644 index ab00c0073..000000000 --- a/OpenKeychain/src/main/res/layout/drawer_list.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -         android:layout_gravity="start" tells DrawerLayout to treat -         this as a sliding drawer on the left side for left-to-right -         languages and on the right side for right-to-left languages. -         The drawer is given a fixed width in dp and extends the full height of -         the container. A solid background is used for contrast -         with the content view. ---> -<ListView xmlns:android="http://schemas.android.com/apk/res/android" -    android:id="@+id/left_drawer" -    android:layout_width="@dimen/drawer_size" -    android:layout_height="match_parent" -    android:layout_gravity="start" -    android:background="@color/white" -    android:choiceMode="singleChoice" -    android:divider="@color/bg_gray" -    android:dividerHeight="1dp" /> diff --git a/OpenKeychain/src/main/res/layout/drawer_list_item.xml b/OpenKeychain/src/main/res/layout/drawer_list_item.xml deleted file mode 100644 index 4719483da..000000000 --- a/OpenKeychain/src/main/res/layout/drawer_list_item.xml +++ /dev/null @@ -1,31 +0,0 @@ -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" -    xmlns:fontawesometext="http://schemas.android.com/apk/res-auto" -    android:layout_width="match_parent" -    android:layout_height="wrap_content"> - -    <ImageView -        android:id="@+id/drawer_item_icon" -        android:gravity="center_vertical" -        android:layout_width="30dp" -        android:layout_height="wrap_content" -        android:layout_marginLeft="8dp" -        android:layout_alignParentStart="true" -        android:layout_alignParentLeft="true" -        android:layout_centerVertical="true" /> - -    <TextView -        android:id="@+id/drawer_item_text" -        android:text="Test" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:gravity="center_vertical" -        android:layout_marginLeft="8dp" -        android:paddingBottom="16dp" -        android:paddingRight="16dp" -        android:paddingTop="16dp" -        android:textAppearance="@android:style/TextAppearance.Medium" -        android:textColor="#111" -        android:layout_alignParentTop="true" -        android:layout_toRightOf="@id/drawer_item_icon"/> - -</RelativeLayout> diff --git a/OpenKeychain/src/main/res/layout/encrypt_files_activity.xml b/OpenKeychain/src/main/res/layout/encrypt_files_activity.xml index cdcf4fa43..401b1c36e 100644 --- a/OpenKeychain/src/main/res/layout/encrypt_files_activity.xml +++ b/OpenKeychain/src/main/res/layout/encrypt_files_activity.xml @@ -1,14 +1,32 @@  <?xml version="1.0" encoding="utf-8"?> -<android.support.v4.widget.FixedDrawerLayout -        xmlns:android="http://schemas.android.com/apk/res/android" -        xmlns:tools="http://schemas.android.com/tools" -        android:id="@+id/drawer_layout" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="match_parent" +    android:layout_height="match_parent"> + +    <include +        android:id="@+id/toolbar" +        layout="@layout/toolbar" /> + +    <LinearLayout +        android:layout_below="@id/toolbar" +        android:id="@+id/content_frame"          android:layout_width="match_parent"          android:layout_height="match_parent" -        tools:context=".ui.EncryptFileActivity"> +        android:orientation="vertical"> + +        <include layout="@layout/notify_area" /> -    <include layout="@layout/encrypt_files_content"/> +        <FrameLayout +            android:id="@+id/encrypt_pager_mode" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:orientation="vertical" /> -    <include layout="@layout/drawer_list" /> +        <fragment +            android:id="@+id/encrypt_file_fragment" +            android:name="org.sufficientlysecure.keychain.ui.EncryptFilesFragment" +            android:layout_width="match_parent" +            android:layout_height="match_parent" /> -</android.support.v4.widget.FixedDrawerLayout>
\ No newline at end of file +    </LinearLayout> +</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/encrypt_files_content.xml b/OpenKeychain/src/main/res/layout/encrypt_files_content.xml deleted file mode 100644 index 61c7e69c9..000000000 --- a/OpenKeychain/src/main/res/layout/encrypt_files_content.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <include -        android:id="@+id/toolbar" -        layout="@layout/toolbar" /> - -    <LinearLayout -        android:layout_below="@id/toolbar" -        android:id="@+id/content_frame" -        android:layout_marginLeft="@dimen/drawer_content_padding" -        android:layout_width="match_parent" -        android:layout_height="match_parent" -        android:orientation="vertical"> - -        <include layout="@layout/notify_area" /> - -        <FrameLayout -            android:id="@+id/encrypt_pager_mode" -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -            android:orientation="vertical" /> - -        <fragment -            android:id="@+id/encrypt_file_fragment" -            android:name="org.sufficientlysecure.keychain.ui.EncryptFilesFragment" -            android:layout_width="match_parent" -            android:layout_height="match_parent" /> - -    </LinearLayout> -</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/encrypt_text_activity.xml b/OpenKeychain/src/main/res/layout/encrypt_text_activity.xml index 5d5e16131..0459fa3d7 100644 --- a/OpenKeychain/src/main/res/layout/encrypt_text_activity.xml +++ b/OpenKeychain/src/main/res/layout/encrypt_text_activity.xml @@ -1,14 +1,32 @@  <?xml version="1.0" encoding="utf-8"?> -<android.support.v4.widget.FixedDrawerLayout -        xmlns:android="http://schemas.android.com/apk/res/android" -        xmlns:tools="http://schemas.android.com/tools" -        android:id="@+id/drawer_layout" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="match_parent" +    android:layout_height="match_parent"> + +    <include +        android:id="@+id/toolbar" +        layout="@layout/toolbar" /> + +    <LinearLayout +        android:layout_below="@id/toolbar" +        android:id="@+id/content_frame"          android:layout_width="match_parent"          android:layout_height="match_parent" -        tools:context=".ui.EncryptTextActivity"> +        android:orientation="vertical"> + +        <include layout="@layout/notify_area" /> -    <include layout="@layout/encrypt_text_content"/> +        <FrameLayout +            android:id="@+id/encrypt_pager_mode" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:orientation="vertical" /> -    <include layout="@layout/drawer_list" /> +        <fragment +            android:id="@+id/encrypt_text_fragment" +            android:name="org.sufficientlysecure.keychain.ui.EncryptTextFragment" +            android:layout_width="match_parent" +            android:layout_height="match_parent" /> -</android.support.v4.widget.FixedDrawerLayout>
\ No newline at end of file +    </LinearLayout> +</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/encrypt_text_content.xml b/OpenKeychain/src/main/res/layout/encrypt_text_content.xml deleted file mode 100644 index 31ebef798..000000000 --- a/OpenKeychain/src/main/res/layout/encrypt_text_content.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <include -        android:id="@+id/toolbar" -        layout="@layout/toolbar" /> - -    <LinearLayout -        android:layout_below="@id/toolbar" -        android:id="@+id/content_frame" -        android:layout_marginLeft="@dimen/drawer_content_padding" -        android:layout_width="match_parent" -        android:layout_height="match_parent" -        android:orientation="vertical"> - -        <include layout="@layout/notify_area" /> - -        <FrameLayout -            android:id="@+id/encrypt_pager_mode" -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -            android:orientation="vertical" /> - -        <fragment -            android:id="@+id/encrypt_text_fragment" -            android:name="org.sufficientlysecure.keychain.ui.EncryptTextFragment" -            android:layout_width="match_parent" -            android:layout_height="match_parent" /> - -    </LinearLayout> -</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/key_list_activity.xml b/OpenKeychain/src/main/res/layout/key_list_activity.xml deleted file mode 100644 index 03ef85381..000000000 --- a/OpenKeychain/src/main/res/layout/key_list_activity.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<android.support.v4.widget.FixedDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:id="@+id/drawer_layout" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <include layout="@layout/key_list_content" /> - -    <include layout="@layout/drawer_list" /> - -</android.support.v4.widget.FixedDrawerLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/key_list_content.xml b/OpenKeychain/src/main/res/layout/key_list_content.xml index 7179793b5..293c45e1c 100644 --- a/OpenKeychain/src/main/res/layout/key_list_content.xml +++ b/OpenKeychain/src/main/res/layout/key_list_content.xml @@ -17,7 +17,6 @@          <LinearLayout              android:id="@+id/content_frame" -            android:layout_marginLeft="@dimen/drawer_content_padding"              android:orientation="vertical"              android:layout_width="match_parent"              android:layout_height="match_parent"> diff --git a/OpenKeychain/src/main/res/values-large/dimens.xml b/OpenKeychain/src/main/res/values-large/dimens.xml index 192a4bb99..045e125f3 100644 --- a/OpenKeychain/src/main/res/values-large/dimens.xml +++ b/OpenKeychain/src/main/res/values-large/dimens.xml @@ -1,4 +1,3 @@  <?xml version="1.0" encoding="utf-8"?>  <resources> -    <dimen name="drawer_content_padding">240dp</dimen>  </resources> diff --git a/OpenKeychain/src/main/res/values/colors.xml b/OpenKeychain/src/main/res/values/colors.xml index 353e33058..fbbb89ed1 100644 --- a/OpenKeychain/src/main/res/values/colors.xml +++ b/OpenKeychain/src/main/res/values/colors.xml @@ -19,13 +19,12 @@      <color name="primary">#4CAF50</color>      <color name="primary_dark">#388E3C</color>      <color name="primary_light">#C8E6C9</color> -    <color name="accent">#536DFE</color> +    <color name="accent">#00BCD4</color>      <color name="primary_text">#212121</color>      <color name="secondary_text">#727272</color>      <color name="icons">#FFFFFF</color>      <color name="divider">#B6B6B6</color> -      <!-- set to text colors -->      <color name="emphasis">#212121</color>      <color name="emphasis_dark">#727272</color> diff --git a/OpenKeychain/src/main/res/values/dimens.xml b/OpenKeychain/src/main/res/values/dimens.xml index e1a7749f0..55344e519 100644 --- a/OpenKeychain/src/main/res/values/dimens.xml +++ b/OpenKeychain/src/main/res/values/dimens.xml @@ -1,5 +1,3 @@  <?xml version="1.0" encoding="utf-8"?>  <resources> -    <dimen name="drawer_size">240dp</dimen> -    <dimen name="drawer_content_padding">0dp</dimen>  </resources>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 794de34f0..ea2e02e1c 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -489,6 +489,7 @@      <string name="api_settings_package_name">"Package Name"</string>      <string name="api_settings_package_signature">"SHA-256 of Package Signature"</string>      <string name="api_settings_accounts">"Accounts"</string> +    <string name="api_settings_allowed_keys">"Allowed Keys"</string>      <string name="api_settings_settings">"Settings"</string>      <string name="api_settings_key">"Account key:"</string>      <string name="api_settings_accounts_empty">"No accounts attached to this app."</string> diff --git a/OpenKeychain/src/main/res/values/themes.xml b/OpenKeychain/src/main/res/values/themes.xml index 3b7d66963..075f538e6 100644 --- a/OpenKeychain/src/main/res/values/themes.xml +++ b/OpenKeychain/src/main/res/values/themes.xml @@ -3,18 +3,22 @@      <style name="KeychainTheme" parent="KeychainTheme.Base" /> -    <style name="KeychainTheme.Base" parent="HoloKeychainTheme"> +    <!--<style name="KeychainTheme.Base" parent="HoloKeychainTheme">--> +    <style name="KeychainTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">          <item name="colorPrimary">@color/colorPrimary</item>          <item name="colorPrimaryDark">@color/colorPrimary</item> +        <item name="colorAccent">@color/accent</item> +          <item name="android:windowNoTitle">true</item>          <item name="windowActionBar">false</item>          <item name="searchViewStyle">@style/MySearchViewStyle</item> -        <item name="colorAccent">@color/accent</item>          <!-- Navigation Drawer library --> -        <item name="drawerType">@integer/DRAWERTYPE_CUSTOM</item> +        <item name="drawerType">@integer/DRAWERTYPE_IMAGE</item> +        <!-- dark action bar... -->          <item name="theme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item> -        <item name="popupTheme">@style/Base.V21.Theme.AppCompat.Light.Dialog</item> +        <!-- ...but light popup menu (white background) --> +        <item name="popupTheme">@style/ThemeOverlay.AppCompat.Light</item>          <item name="drawerColor">#fafafa</item>          <item name="singleAccount">false</item>          <item name="sectionStyle">@style/MaterialSectionTheme.Light</item> | 
