More experimental work on api accounts

This commit is contained in:
Dominik Schürmann 2014-03-25 21:23:59 +01:00
parent 031beed680
commit cff35ca842
9 changed files with 120 additions and 86 deletions

View File

@ -95,8 +95,6 @@ public class KeychainContract {
public static final String BASE_API_APPS = "api_apps";
public static final String PATH_ACCOUNTS = "accounts";
public static final String PATH_BY_PACKAGE_NAME = "package_name";
public static class KeyRings implements KeyRingsColumns, BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
@ -272,13 +270,12 @@ public class KeychainContract {
*/
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api.apps";
public static Uri buildIdUri(String rowId) {
return CONTENT_URI.buildUpon().appendPath(rowId).build();
}
// public static Uri buildIdUri(String rowId) {
// return CONTENT_URI.buildUpon().appendPath(rowId).build();
// }
public static Uri buildByPackageNameUri(String packageName) {
return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME)
.appendEncodedPath(packageName).build();
return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build();
}
}

View File

@ -80,10 +80,8 @@ public class KeychainProvider extends ContentProvider {
private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222;
private static final int API_APPS = 301;
private static final int API_APPS_BY_ROW_ID = 302;
private static final int API_APPS_BY_PACKAGE_NAME = 303;
private static final int API_ACCOUNTS = 304;
private static final int API_ACCOUNTS_BY_ROW_ID = 305;
private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306;
private static final int UNIFIED_KEY_RING = 401;
@ -241,19 +239,22 @@ public class KeychainProvider extends ContentProvider {
/**
* API apps
*
* <pre>
* api_apps
* api_apps/_
*
* api_apps/_/accounts
* api_apps/_/accounts/_
* </pre>
*/
matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS);
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID);
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/"
+ KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME);
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME);
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/"
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS);
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/"
+ KeychainContract.PATH_ACCOUNTS + "/#", API_ACCOUNTS_BY_ROW_ID);
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/"
+ KeychainContract.PATH_ACCOUNTS + "/"
+ KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME);
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME);
/**
* data stream
@ -322,14 +323,12 @@ public class KeychainProvider extends ContentProvider {
case API_APPS:
return ApiApps.CONTENT_TYPE;
case API_APPS_BY_ROW_ID:
case API_APPS_BY_PACKAGE_NAME:
return ApiApps.CONTENT_ITEM_TYPE;
case API_ACCOUNTS:
return ApiAccounts.CONTENT_TYPE;
case API_ACCOUNTS_BY_ROW_ID:
case API_ACCOUNTS_BY_ACCOUNT_NAME:
return ApiAccounts.CONTENT_ITEM_TYPE;
@ -679,13 +678,6 @@ public class KeychainProvider extends ContentProvider {
case API_APPS:
qb.setTables(Tables.API_APPS);
break;
case API_APPS_BY_ROW_ID:
qb.setTables(Tables.API_APPS);
qb.appendWhere(BaseColumns._ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
case API_APPS_BY_PACKAGE_NAME:
qb.setTables(Tables.API_APPS);
@ -696,17 +688,6 @@ public class KeychainProvider extends ContentProvider {
case API_ACCOUNTS:
qb.setTables(Tables.API_ACCOUNTS);
break;
case API_ACCOUNTS_BY_ROW_ID:
qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "("
+ Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "."
+ ApiAccounts.PACKAGE_NAME_FK + " )");
qb.appendWhere(Tables.API_APPS + "." + BaseColumns._ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + BaseColumns._ID + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
case API_ACCOUNTS_BY_ACCOUNT_NAME:
qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "("
@ -812,7 +793,7 @@ public class KeychainProvider extends ContentProvider {
break;
case API_APPS:
rowId = db.insertOrThrow(Tables.API_APPS, null, values);
rowUri = ApiApps.buildIdUri(Long.toString(rowId));
// rowUri = ApiApps.buildIdUri(Long.toString(rowId));
break;
case API_ACCOUNTS:
@ -878,18 +859,10 @@ public class KeychainProvider extends ContentProvider {
count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
selectionArgs);
break;
case API_APPS_BY_ROW_ID:
count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection),
selectionArgs);
break;
case API_APPS_BY_PACKAGE_NAME:
count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection),
selectionArgs);
break;
case API_ACCOUNTS_BY_ROW_ID:
count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, false, selection),
selectionArgs);
break;
case API_ACCOUNTS_BY_ACCOUNT_NAME:
count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, true, selection),
selectionArgs);
@ -956,18 +929,10 @@ public class KeychainProvider extends ContentProvider {
count = db.update(Tables.USER_IDS, values,
buildDefaultUserIdsSelection(uri, selection), selectionArgs);
break;
case API_APPS_BY_ROW_ID:
count = db.update(Tables.API_APPS, values,
buildDefaultApiAppsSelection(uri, false, selection), selectionArgs);
break;
case API_APPS_BY_PACKAGE_NAME:
count = db.update(Tables.API_APPS, values,
buildDefaultApiAppsSelection(uri, true, selection), selectionArgs);
break;
case API_ACCOUNTS_BY_ROW_ID:
count = db.update(Tables.API_ACCOUNTS, values,
buildDefaultApiAccountsSelection(uri, false, selection), selectionArgs);
break;
case API_ACCOUNTS_BY_ACCOUNT_NAME:
count = db.update(Tables.API_ACCOUNTS, values,
buildDefaultApiAccountsSelection(uri, true, selection), selectionArgs);

View File

@ -19,10 +19,7 @@ package org.sufficientlysecure.keychain.remote.ui;
import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
@ -31,15 +28,10 @@ import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
@ -50,36 +42,46 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
public class AccountsListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
private static final String ARG_DATA_URI = "uri";
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
private String mPackageName;
private Uri mDataUri;
public String getPackageName() {
return mPackageName;
}
/**
* Creates new instance of this fragment
*/
public static AccountsListFragment newInstance(Uri dataUri) {
AccountsListFragment frag = new AccountsListFragment();
public void setPackageName(String packageName) {
this.mPackageName = packageName;
Bundle args = new Bundle();
args.putParcelable(ARG_DATA_URI, dataUri);
frag.setArguments(args);
return frag;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mDataUri = getArguments().getParcelable(ARG_DATA_URI);
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
// edit app settings
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
intent.setData(ContentUris.withAppendedId(ApiApps.CONTENT_URI, id));
startActivity(intent);
// // edit app settings
// Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
// intent.setData(ContentUris.withAppendedId(ApiApps.CONTENT_URI, id));
// startActivity(intent);
}
});
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText(getString(R.string.api_no_apps));
setEmptyText(getString(R.string.api_settings_accounts_empty));
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
@ -108,11 +110,11 @@ public class AccountsListFragment extends ListFragment implements
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(mPackageName);
// Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(mPackageName);
// 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, null, null,
return new CursorLoader(getActivity(), mDataUri, PROJECTION, null, null,
KeychainContract.ApiAccounts.ACCOUNT_NAME + " COLLATE LOCALIZED ASC");
}

View File

@ -24,9 +24,11 @@ import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.util.Log;
@ -35,6 +37,7 @@ public class AppSettingsActivity extends ActionBarActivity {
private Uri mAppUri;
private AppSettingsFragment mSettingsFragment;
private AccountsListFragment mAccountsListFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -64,7 +67,7 @@ public class AppSettingsActivity extends ActionBarActivity {
return;
} else {
Log.d(Constants.TAG, "uri: " + mAppUri);
loadData(mAppUri);
loadData(savedInstanceState, mAppUri);
}
}
@ -88,9 +91,34 @@ public class AppSettingsActivity extends ActionBarActivity {
return super.onOptionsItemSelected(item);
}
private void loadData(Uri appUri) {
private void loadData(Bundle savedInstanceState, Uri appUri) {
// TODO: load this also like other fragment with newInstance arguments?
AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
mSettingsFragment.setAppSettings(settings);
Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build();
Log.d(Constants.TAG, "accountsUri: " + accountsUri);
startListFragment(savedInstanceState, accountsUri);
}
private void startListFragment(Bundle savedInstanceState, Uri dataUri) {
// 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
mAccountsListFragment = AccountsListFragment.newInstance(dataUri);
// 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();
// do it immediately!
getSupportFragmentManager().executePendingTransactions();
}
private void revokeAccess() {

View File

@ -17,7 +17,6 @@
package org.sufficientlysecure.keychain.remote.ui;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@ -38,9 +37,11 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.util.Log;
public class AppsListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
@ -55,9 +56,10 @@ public class AppsListFragment extends ListFragment implements
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
String selectedPackageName = mAdapter.getItemPackageName(position);
// edit app settings
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
intent.setData(ContentUris.withAppendedId(KeychainContract.ApiApps.CONTENT_URI, id));
intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName));
startActivity(intent);
}
});
@ -79,7 +81,10 @@ public class AppsListFragment extends ListFragment implements
}
// These are the Contacts rows that we will retrieve.
static final String[] PROJECTION = new String[]{ApiApps._ID, ApiApps.PACKAGE_NAME};
static final String[] PROJECTION = new String[]{
ApiApps._ID, // 0
ApiApps.PACKAGE_NAME // 1
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
@ -119,6 +124,25 @@ public class AppsListFragment extends ListFragment implements
mPM = context.getApplicationContext().getPackageManager();
}
/**
* Similar to CursorAdapter.getItemId().
* Required to build Uris for api app view, which is not based on row ids
*
* @param position
* @return
*/
public String getItemPackageName(int position) {
if (mDataValid && mCursor != null) {
if (mCursor.moveToPosition(position)) {
return mCursor.getString(1);
} else {
return null;
}
} else {
return null;
}
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);

View File

@ -46,8 +46,8 @@
<org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:foldedLabel="@string/btn_encryption_advanced_settings_show"
custom:unFoldedLabel="@string/btn_encryption_advanced_settings_hide"
custom:foldedLabel="@string/api_settings_show_advanced"
custom:unFoldedLabel="@string/api_settings_hide_advanced"
custom:foldedIcon="fa-chevron-right"
custom:unFoldedIcon="fa-chevron-down">

View File

@ -12,10 +12,24 @@
<fragment
android:id="@+id/api_app_settings_fragment"
android:name="org.sufficientlysecure.keychain.service.remote.AppSettingsFragment"
android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout="@layout/api_app_settings_fragment" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/api_settings_accounts"
android:textAppearance="?android:attr/textAppearanceMedium" />
<FrameLayout
android:id="@+id/api_accounts_list_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="4dp"
android:paddingRight="4dp" />
</LinearLayout>
</ScrollView>

View File

@ -39,8 +39,8 @@
<org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:foldedLabel="@string/btn_encryption_advanced_settings_show"
custom:unFoldedLabel="@string/btn_encryption_advanced_settings_hide"
custom:foldedLabel="@string/api_settings_show_info"
custom:unFoldedLabel="@string/api_settings_hide_info"
custom:foldedIcon="fa-chevron-right"
custom:unFoldedIcon="fa-chevron-down">

View File

@ -412,6 +412,8 @@
<!-- Remote API -->
<string name="api_no_apps">No registered applications!\n\nThird-party applications can request access to OpenKeychain. After granting access, they will be listed here.</string>
<string name="api_settings_show_info">Show advanced information</string>
<string name="api_settings_hide_info">Hide advanced information</string>
<string name="api_settings_show_advanced">Show advanced settings</string>
<string name="api_settings_hide_advanced">Hide advanced settings</string>
<string name="api_settings_no_key">No key selected</string>
@ -421,6 +423,8 @@
<string name="api_settings_revoke">Revoke access</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_accounts">Accounts</string>
<string name="api_settings_accounts_empty">No accounts attached to this application.</string>
<string name="api_register_text">The displayed application requests access to OpenKeychain.\nAllow access?\n\nWARNING: If you do not know why this screen appeared, disallow access! You can revoke access later using the \'Registered Applications\' screen.</string>
<string name="api_register_allow">Allow access</string>
<string name="api_register_disallow">Disallow access</string>