diff options
| author | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-01-29 21:00:58 +0100 | 
|---|---|---|
| committer | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-01-29 21:00:58 +0100 | 
| commit | 38f7950d90c49ff27dcf2afb651337c2f4f2b663 (patch) | |
| tree | aa882e6edec94e73f6a38df005604dce1b4441f9 /OpenKeychain/src | |
| parent | d6e4936fa54dc3577296dbadaeb556178dcad2c3 (diff) | |
| download | open-keychain-38f7950d90c49ff27dcf2afb651337c2f4f2b663.tar.gz open-keychain-38f7950d90c49ff27dcf2afb651337c2f4f2b663.tar.bz2 open-keychain-38f7950d90c49ff27dcf2afb651337c2f4f2b663.zip  | |
Saving of allowed keys
Diffstat (limited to 'OpenKeychain/src')
11 files changed, 467 insertions, 232 deletions
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index ff77e745b..d47729127 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -142,11 +142,6 @@                  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" 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 e0313074c..5856589c4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -321,12 +321,6 @@ public class KeychainContract {          public static final String CONTENT_TYPE                  = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.api_apps.allowed_keys"; -        /** -         * Use if a single item is returned -         */ -        public static final String CONTENT_ITEM_TYPE -                = "vnd.android.cursor.item/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(); 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 9e8f03a66..d34cc74a3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -174,7 +174,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {                  + ApiAppsAllowedKeysColumns.KEY_ID + " INTEGER, "                  + ApiAppsAllowedKeysColumns.PACKAGE_NAME + " TEXT NOT NULL, " -                + "UNIQUE(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + "), " +                + "UNIQUE(" + ApiAppsAllowedKeysColumns.KEY_ID + ", " +                + ApiAppsAllowedKeysColumns.PACKAGE_NAME + "), "                  + "FOREIGN KEY(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") REFERENCES "                  + Tables.API_APPS + "(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") ON DELETE CASCADE"                  + ")"; @@ -208,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 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 4f263afc4..6bd88570f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -231,7 +231,7 @@ public class KeychainProvider extends ContentProvider {                  return ApiAccounts.CONTENT_ITEM_TYPE;              case API_ALLOWED_KEYS: -                return ApiAllowedKeys.CONTENT_ITEM_TYPE; +                return ApiAllowedKeys.CONTENT_TYPE;              default:                  throw new UnsupportedOperationException("Unknown uri: " + uri); @@ -717,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); @@ -725,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;              } @@ -793,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);          } @@ -885,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 a229f454f..db458254c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;  import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;  import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.remote.ui.AppSettingsAllowedKeys;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;  import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;  import org.sufficientlysecure.keychain.util.Preferences; @@ -50,6 +51,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; @@ -1504,6 +1506,44 @@ 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 { +        ArrayList<ContentProviderOperation> ops = new ArrayList<>(); + +        // clear table +        ops.add(ContentProviderOperation.newDelete(uri) +                .build()); + +        // re-insert allowed key ids +        for (Long keyId : allowedKeyIds) { +            ops.add(ContentProviderOperation.newInsert(uri) +                    .withValue(ApiAllowedKeys.KEY_ID, keyId) +                    .build()); +        } + +        getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY, ops); +    } +      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/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java index 36d6ad888..b73055670 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; @@ -39,6 +40,7 @@ public class AppSettingsActivity extends BaseActivity {      private AppSettingsFragment mSettingsFragment;      private AccountsListFragment mAccountsListFragment; +    private AppSettingsAllowedKeys mAllowedKeysFragment;      // model      AppSettings mAppSettings; @@ -47,6 +49,20 @@ public class AppSettingsActivity extends BaseActivity {      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState); +        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 = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(                  R.id.api_app_settings_fragment); @@ -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 = AppSettingsAllowedKeys.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/AppSettingsAllowedKeys.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeys.java new file mode 100644 index 000000000..3e4394264 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeys.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 AppSettingsAllowedKeys 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 AppSettingsAllowedKeys newInstance(Uri dataUri) { +        AppSettingsAllowedKeys frag = new AppSettingsAllowedKeys(); +        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/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/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/layout/api_app_settings_activity.xml b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml index 800e2eb87..63fc060f4 100644 --- a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml +++ b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml @@ -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/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 1151644d8..97054076c 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>  | 
