Saving of allowed keys

This commit is contained in:
Dominik Schürmann 2015-01-29 21:00:58 +01:00
parent d6e4936fa5
commit 38f7950d90
12 changed files with 471 additions and 236 deletions

View File

@ -141,11 +141,6 @@
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.MainActivity" /> android:value=".ui.MainActivity" />
</activity> </activity>
<activity
android:name=".ui.SelectPublicKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_select_recipients"
android:launchMode="singleTop" />
<activity <activity
android:name=".ui.EncryptFilesActivity" android:name=".ui.EncryptFilesActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"

View File

@ -321,12 +321,6 @@ public class KeychainContract {
public static final String CONTENT_TYPE public static final String CONTENT_TYPE
= "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.api_apps.allowed_keys"; = "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) { public static Uri buildBaseUri(String packageName) {
return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ALLOWED_KEYS) return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ALLOWED_KEYS)
.build(); .build();

View File

@ -174,7 +174,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ ApiAppsAllowedKeysColumns.KEY_ID + " INTEGER, " + ApiAppsAllowedKeysColumns.KEY_ID + " INTEGER, "
+ ApiAppsAllowedKeysColumns.PACKAGE_NAME + " TEXT NOT NULL, " + ApiAppsAllowedKeysColumns.PACKAGE_NAME + " TEXT NOT NULL, "
+ "UNIQUE(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + "), " + "UNIQUE(" + ApiAppsAllowedKeysColumns.KEY_ID + ", "
+ ApiAppsAllowedKeysColumns.PACKAGE_NAME + "), "
+ "FOREIGN KEY(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") REFERENCES " + "FOREIGN KEY(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") REFERENCES "
+ Tables.API_APPS + "(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") ON DELETE CASCADE" + 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_CERTS);
db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS);
db.execSQL(CREATE_API_APPS_ACCOUNTS); db.execSQL(CREATE_API_APPS_ACCOUNTS);
db.execSQL(CREATE_API_APPS_ALLOWED_KEYS);
} }
@Override @Override

View File

@ -231,7 +231,7 @@ public class KeychainProvider extends ContentProvider {
return ApiAccounts.CONTENT_ITEM_TYPE; return ApiAccounts.CONTENT_ITEM_TYPE;
case API_ALLOWED_KEYS: case API_ALLOWED_KEYS:
return ApiAllowedKeys.CONTENT_ITEM_TYPE; return ApiAllowedKeys.CONTENT_TYPE;
default: default:
throw new UnsupportedOperationException("Unknown uri: " + uri); throw new UnsupportedOperationException("Unknown uri: " + uri);
@ -717,7 +717,7 @@ public class KeychainProvider extends ContentProvider {
db.insertOrThrow(Tables.API_APPS, null, values); db.insertOrThrow(Tables.API_APPS, null, values);
break; break;
case API_ACCOUNTS: case API_ACCOUNTS: {
// set foreign key automatically based on given uri // set foreign key automatically based on given uri
// e.g., api_apps/com.example.app/accounts/ // e.g., api_apps/com.example.app/accounts/
String packageName = uri.getPathSegments().get(1); String packageName = uri.getPathSegments().get(1);
@ -725,12 +725,21 @@ public class KeychainProvider extends ContentProvider {
db.insertOrThrow(Tables.API_ACCOUNTS, null, values); db.insertOrThrow(Tables.API_ACCOUNTS, null, values);
break; 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: default:
throw new UnsupportedOperationException("Unknown uri: " + uri); throw new UnsupportedOperationException("Unknown uri: " + uri);
} }
if(keyId != null) { if (keyId != null) {
uri = KeyRings.buildGenericKeyRingUri(keyId); uri = KeyRings.buildGenericKeyRingUri(keyId);
rowUri = uri; rowUri = uri;
} }
@ -793,6 +802,10 @@ public class KeychainProvider extends ContentProvider {
count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, additionalSelection), count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, additionalSelection),
selectionArgs); selectionArgs);
break; break;
case API_ALLOWED_KEYS:
count = db.delete(Tables.API_ALLOWED_KEYS, buildDefaultApiAllowedKeysSelection(uri, additionalSelection),
selectionArgs);
break;
default: default:
throw new UnsupportedOperationException("Unknown uri: " + uri); throw new UnsupportedOperationException("Unknown uri: " + uri);
} }
@ -885,4 +898,15 @@ public class KeychainProvider extends ContentProvider {
+ andSelection; + 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;
}
} }

View File

@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; 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.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Preferences; 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.UncachedPublicKey;
import org.sufficientlysecure.keychain.pgp.WrappedSignature; import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; 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.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
@ -1504,6 +1506,44 @@ public class ProviderHelper {
return keyIds; 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) { public Set<String> getAllFingerprints(Uri uri) {
Set<String> fingerprints = new HashSet<>(); Set<String> fingerprints = new HashSet<>();
String[] projection = new String[]{KeyRings.FINGERPRINT}; String[] projection = new String[]{KeyRings.FINGERPRINT};

View File

@ -24,6 +24,7 @@ import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
@ -39,6 +40,7 @@ public class AppSettingsActivity extends BaseActivity {
private AppSettingsFragment mSettingsFragment; private AppSettingsFragment mSettingsFragment;
private AccountsListFragment mAccountsListFragment; private AccountsListFragment mAccountsListFragment;
private AppSettingsAllowedKeys mAllowedKeysFragment;
// model // model
AppSettings mAppSettings; AppSettings mAppSettings;
@ -47,6 +49,20 @@ public class AppSettingsActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(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( mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
R.id.api_app_settings_fragment); R.id.api_app_settings_fragment);
@ -62,6 +78,10 @@ public class AppSettingsActivity extends BaseActivity {
} }
} }
private void save() {
mAllowedKeysFragment.saveAllowedKeys();
}
@Override @Override
protected void initLayout() { protected void initLayout() {
setContentView(R.layout.api_app_settings_activity); 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(); Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build();
Log.d(Constants.TAG, "accountsUri: " + accountsUri); 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, // However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else // then we don't need to do anything and should return or else
// we could end up with overlapping fragments. // we could end up with overlapping fragments.
@ -132,13 +154,17 @@ public class AppSettingsActivity extends BaseActivity {
} }
// Create an instance of the fragment // 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 // Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes! // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.replace(R.id.api_accounts_list_fragment, mAccountsListFragment) .replace(R.id.api_accounts_list_fragment, mAccountsListFragment)
.commitAllowingStateLoss(); .commitAllowingStateLoss();
getSupportFragmentManager().beginTransaction()
.replace(R.id.api_allowed_keys_list_fragment, mAllowedKeysFragment)
.commitAllowingStateLoss();
// do it immediately! // do it immediately!
getSupportFragmentManager().executePendingTransactions(); getSupportFragmentManager().executePendingTransactions();
} }

View File

@ -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);
}
}
}

View File

@ -110,7 +110,6 @@ public class KeyListFragment extends LoaderFragment
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mExportHelper = new ExportHelper(getActivity()); mExportHelper = new ExportHelper(getActivity());
} }
/** /**
@ -205,9 +204,8 @@ public class KeyListFragment extends LoaderFragment
mStickyList.setFastScrollEnabled(true); 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.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
@ -282,7 +280,6 @@ public class KeyListFragment extends LoaderFragment
} }
}); });
}
// We have a menu item to show in action bar. // We have a menu item to show in action bar.
setHasOptionsMenu(true); setHasOptionsMenu(true);
@ -369,10 +366,8 @@ public class KeyListFragment extends LoaderFragment
// end action mode, if any // end action mode, if any
if (mActionMode != null) { if (mActionMode != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mActionMode.finish(); mActionMode.finish();
} }
}
// The list should now be shown. // The list should now be shown.
if (isResumed()) { if (isResumed()) {
@ -401,7 +396,6 @@ public class KeyListFragment extends LoaderFragment
startActivity(viewIntent); startActivity(viewIntent);
} }
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
protected void encrypt(ActionMode mode, long[] masterKeyIds) { protected void encrypt(ActionMode mode, long[] masterKeyIds) {
Intent intent = new Intent(getActivity(), EncryptFilesActivity.class); Intent intent = new Intent(getActivity(), EncryptFilesActivity.class);
intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA); intent.setAction(EncryptFilesActivity.ACTION_ENCRYPT_DATA);
@ -418,7 +412,6 @@ public class KeyListFragment extends LoaderFragment
* @param masterKeyIds * @param masterKeyIds
* @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not * @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not
*/ */
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) { public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {
// Can only work on singular secret keys // Can only work on singular secret keys
if (hasSecret && masterKeyIds.length > 1) { if (hasSecret && masterKeyIds.length > 1) {

View File

@ -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();
}
}

View File

@ -33,6 +33,18 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:layout="@layout/api_app_settings_fragment" /> tools:layout="@layout/api_app_settings_fragment" />
<TextView
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 <TextView
style="@style/SectionHeader" style="@style/SectionHeader"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -489,6 +489,7 @@
<string name="api_settings_package_name">"Package Name"</string> <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_package_signature">"SHA-256 of Package Signature"</string>
<string name="api_settings_accounts">"Accounts"</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_settings">"Settings"</string>
<string name="api_settings_key">"Account key:"</string> <string name="api_settings_key">"Account key:"</string>
<string name="api_settings_accounts_empty">"No accounts attached to this app."</string> <string name="api_settings_accounts_empty">"No accounts attached to this app."</string>

@ -1 +1 @@
Subproject commit c8766a788884d331c0b61eb74241abd65e9b90b9 Subproject commit dccef45dc1646d623c01dcde7a28711b2ac815e4