use SQLDelight, remove ApiApps access from KeychainProvider

This commit is contained in:
Vincent Breitmoser
2018-06-15 19:46:55 +02:00
parent 59c9f52e85
commit d133b732e5
30 changed files with 628 additions and 962 deletions

View File

@@ -138,7 +138,7 @@ public class ApiPendingIntentFactory {
PendingIntent createSelectSignKeyIdLegacyPendingIntent(Intent data, String packageName, String preferredUserId) {
Intent intent = new Intent(mContext, SelectSignKeyIdActivity.class);
intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(packageName));
intent.putExtra(SelectSignKeyIdActivity.EXTRA_PACKAGE_NAME, packageName);
intent.putExtra(SelectSignKeyIdActivity.EXTRA_USER_ID, preferredUserId);
return createInternal(data, intent);
@@ -147,7 +147,6 @@ public class ApiPendingIntentFactory {
PendingIntent createSelectSignKeyIdPendingIntent(Intent data, String packageName,
byte[] packageSignature, String preferredUserId, boolean showAutocryptHint) {
Intent intent = new Intent(mContext, RemoteSelectIdKeyActivity.class);
intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(packageName));
intent.putExtra(RemoteSelectIdKeyActivity.EXTRA_PACKAGE_NAME, packageName);
intent.putExtra(RemoteSelectIdKeyActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
intent.putExtra(RemoteSelectIdKeyActivity.EXTRA_USER_ID, preferredUserId);
@@ -158,7 +157,6 @@ public class ApiPendingIntentFactory {
PendingIntent createSelectAuthenticationKeyIdPendingIntent(Intent data, String packageName) {
Intent intent = new Intent(mContext, RemoteSelectAuthenticationKeyActivity.class);
intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(packageName));
intent.putExtra(RemoteSelectAuthenticationKeyActivity.EXTRA_PACKAGE_NAME, packageName);
return createInternal(data, intent);

View File

@@ -1,50 +0,0 @@
/*
* Copyright (C) 2017 Schürmann & Breitmoser GbR
*
* 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;
public class AppSettings {
private String mPackageName;
private byte[] mPackageCertificate;
public AppSettings() {
}
public AppSettings(String packageName, byte[] packageSignature) {
super();
this.mPackageName = packageName;
this.mPackageCertificate = packageSignature;
}
public String getPackageName() {
return mPackageName;
}
public void setPackageName(String packageName) {
this.mPackageName = packageName;
}
public byte[] getPackageCertificate() {
return mPackageCertificate;
}
public void setPackageCertificate(byte[] packageCertificate) {
this.mPackageCertificate = packageCertificate;
}
}

View File

@@ -23,6 +23,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -42,7 +43,6 @@ import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptRecommendationResult;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptState;
import org.sufficientlysecure.keychain.provider.DatabaseNotifyManager;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
@@ -54,7 +54,6 @@ import org.sufficientlysecure.keychain.provider.KeychainExternalContract.Autocry
import org.sufficientlysecure.keychain.provider.KeychainExternalContract.EmailStatus;
import org.sufficientlysecure.keychain.provider.KeychainProvider;
import org.sufficientlysecure.keychain.provider.SimpleContentResolverInterface;
import org.sufficientlysecure.keychain.util.CloseDatabaseCursorFactory;
import timber.log.Timber;
@@ -64,9 +63,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
private static final int AUTOCRYPT_STATUS = 201;
private static final int AUTOCRYPT_STATUS_INTERNAL = 202;
private static final int API_APPS = 301;
private static final int API_APPS_BY_PACKAGE_NAME = 302;
public static final String TEMP_TABLE_QUERIED_ADDRESSES = "queried_addresses";
public static final String TEMP_TABLE_COLUMN_ADDRES = "address";
@@ -113,7 +109,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
internalKeychainProvider = new KeychainProvider();
internalKeychainProvider.attachInfo(context, null);
apiPermissionHelper = new ApiPermissionHelper(context, new ApiDataAccessObject(internalKeychainProvider));
apiPermissionHelper = new ApiPermissionHelper(context, new ApiDataAccessObject(getContext()));
databaseNotifyManager = DatabaseNotifyManager.create(context);
return true;
}
@@ -127,13 +123,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
switch (match) {
case EMAIL_STATUS:
return EmailStatus.CONTENT_TYPE;
case API_APPS:
return ApiApps.CONTENT_TYPE;
case API_APPS_BY_PACKAGE_NAME:
return ApiApps.CONTENT_ITEM_TYPE;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
@@ -154,7 +143,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
String groupBy = null;
SQLiteDatabase db = new KeychainDatabase(getContext()).getReadableDatabase();
SupportSQLiteDatabase db = new KeychainDatabase(getContext()).getReadableDatabase();
String callingPackageName = apiPermissionHelper.getCurrentCallingPackage();
@@ -169,7 +158,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
ContentValues cv = new ContentValues();
for (String address : selectionArgs) {
cv.put(TEMP_TABLE_COLUMN_ADDRES, address);
db.insert(TEMP_TABLE_QUERIED_ADDRESSES, null, cv);
db.insert(TEMP_TABLE_QUERIED_ADDRESSES, SQLiteDatabase.CONFLICT_FAIL, cv);
}
HashMap<String, String> projectionMap = new HashMap<>();
@@ -256,7 +245,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
ContentValues cv = new ContentValues();
for (String address : selectionArgs) {
cv.put(TEMP_TABLE_COLUMN_ADDRES, address);
db.insert(TEMP_TABLE_QUERIED_ADDRESSES, null, cv);
db.insert(TEMP_TABLE_QUERIED_ADDRESSES, SQLiteDatabase.CONFLICT_FAIL, cv);
}
boolean isWildcardSelector = selectionArgs.length == 1 && selectionArgs[0].contains("%");
@@ -321,8 +310,8 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
}
qb.setStrict(true);
qb.setCursorFactory(new CloseDatabaseCursorFactory());
Cursor cursor = qb.query(db, projection, null, null, groupBy, null, orderBy);
String query = qb.buildQuery(projection, null, null, groupBy, null, orderBy);
Cursor cursor = db.query(query);
if (cursor != null) {
// Tell the cursor what uri to watch, so it knows when its source data changes
cursor.setNotificationUri(getContext().getContentResolver(), uri);
@@ -337,7 +326,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
return cursor;
}
private void fillTempTableWithAutocryptRecommendations(SQLiteDatabase db,
private void fillTempTableWithAutocryptRecommendations(SupportSQLiteDatabase db,
AutocryptPeerDataAccessObject autocryptPeerDao, String[] peerIds) {
List<AutocryptRecommendationResult> autocryptStates =
autocryptPeerDao.determineAutocryptRecommendations(peerIds);
@@ -345,7 +334,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
fillTempTableWithAutocryptRecommendations(db, autocryptStates);
}
private void fillTempTableWithAutocryptRecommendations(SQLiteDatabase db,
private void fillTempTableWithAutocryptRecommendations(SupportSQLiteDatabase db,
List<AutocryptRecommendationResult> autocryptRecommendations) {
ContentValues cv = new ContentValues();
for (AutocryptRecommendationResult peerResult : autocryptRecommendations) {
@@ -359,12 +348,12 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
KeychainExternalContract.KEY_STATUS_UNVERIFIED);
}
db.update(TEMP_TABLE_QUERIED_ADDRESSES, cv,TEMP_TABLE_COLUMN_ADDRES + "=?",
db.update(TEMP_TABLE_QUERIED_ADDRESSES, SQLiteDatabase.CONFLICT_IGNORE, cv,TEMP_TABLE_COLUMN_ADDRES + "=?",
new String[] { peerResult.peerId });
}
}
private void fillTempTableWithUidResult(SQLiteDatabase db, boolean isWildcardSelector) {
private void fillTempTableWithUidResult(SupportSQLiteDatabase db, boolean isWildcardSelector) {
String cmpOperator = isWildcardSelector ? " LIKE " : " = ";
long unixSeconds = System.currentTimeMillis() / 1000;
db.execSQL("REPLACE INTO " + TEMP_TABLE_QUERIED_ADDRESSES +

View File

@@ -915,8 +915,7 @@ public class OpenPgpService extends Service {
private HashSet<Long> getAllowedKeyIds() {
String currentPkg = mApiPermissionHelper.getCurrentCallingPackage();
return mApiDao.getAllowedKeyIdsForApp(
KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg));
return mApiDao.getAllowedKeyIdsForApp(currentPkg);
}
/**

View File

@@ -17,12 +17,13 @@
package org.sufficientlysecure.keychain.remote;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
public class PackageUninstallReceiver extends BroadcastReceiver {
@@ -34,8 +35,9 @@ public class PackageUninstallReceiver extends BroadcastReceiver {
return;
}
String packageName = uri.getEncodedSchemeSpecificPart();
Uri appUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName);
context.getContentResolver().delete(appUri, null, null);
ApiDataAccessObject apiDao = new ApiDataAccessObject(context);
apiDao.deleteApiApp(packageName);
}
}
}

View File

@@ -17,11 +17,19 @@
package org.sufficientlysecure.keychain.remote;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.openintents.ssh.authentication.ISshAuthenticationService;
@@ -40,7 +48,6 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ssh.AuthenticationData;
@@ -50,13 +57,6 @@ import org.sufficientlysecure.keychain.ssh.AuthenticationResult;
import org.sufficientlysecure.keychain.ssh.signature.SshSignatureConverter;
import timber.log.Timber;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
public class SshAuthenticationService extends Service {
private static final String TAG = "SshAuthService";
@@ -394,7 +394,7 @@ public class SshAuthenticationService extends Service {
private HashSet<Long> getAllowedKeyIds() {
String currentPkg = mApiPermissionHelper.getCurrentCallingPackage();
return mApiDao.getAllowedKeyIdsForApp(KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg));
return mApiDao.getAllowedKeyIdsForApp(currentPkg);
}
/**

View File

@@ -26,7 +26,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.view.Menu;
@@ -36,24 +35,26 @@ import android.widget.TextView;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.model.ApiApp;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.dialog.AdvancedAppSettingsDialogFragment;
import timber.log.Timber;
public class AppSettingsActivity extends BaseActivity {
private Uri mAppUri;
public static final String EXTRA_PACKAGE_NAME = "package_name";
private String packageName;
private TextView mAppNameView;
private ImageView mAppIconView;
// model
AppSettings mAppSettings;
ApiApp mApiApp;
private ApiDataAccessObject apiDataAccessObject;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -68,15 +69,16 @@ public class AppSettingsActivity extends BaseActivity {
setTitle(null);
Intent intent = getIntent();
mAppUri = intent.getData();
if (mAppUri == null) {
Timber.e("Intent data missing. Should be Uri of app!");
packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
if (packageName == null) {
Timber.e("Required extra package_name missing!");
finish();
return;
}
Timber.d("uri: %s", mAppUri);
loadData(savedInstanceState, mAppUri);
apiDataAccessObject = new ApiDataAccessObject(this);
loadData(savedInstanceState);
}
private void save() {
@@ -138,7 +140,7 @@ public class AppSettingsActivity extends BaseActivity {
// advanced info: package certificate SHA-256
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(mAppSettings.getPackageCertificate());
md.update(mApiApp.package_signature());
byte[] digest = md.digest();
certificate = new String(Hex.encode(digest));
} catch (NoSuchAlgorithmException e) {
@@ -146,7 +148,7 @@ public class AppSettingsActivity extends BaseActivity {
}
AdvancedAppSettingsDialogFragment dialogFragment =
AdvancedAppSettingsDialogFragment.newInstance(mAppSettings.getPackageName(), certificate);
AdvancedAppSettingsDialogFragment.newInstance(mApiApp.package_name(), certificate);
dialogFragment.show(getSupportFragmentManager(), "advancedDialog");
}
@@ -155,7 +157,7 @@ public class AppSettingsActivity extends BaseActivity {
Intent i;
PackageManager manager = getPackageManager();
try {
i = manager.getLaunchIntentForPackage(mAppSettings.getPackageName());
i = manager.getLaunchIntentForPackage(mApiApp.package_name());
if (i == null)
throw new PackageManager.NameNotFoundException();
// start like the Android launcher would do
@@ -167,31 +169,29 @@ public class AppSettingsActivity extends BaseActivity {
}
}
private void loadData(Bundle savedInstanceState, Uri appUri) {
mAppSettings = new ApiDataAccessObject(this).getApiAppSettings(appUri);
private void loadData(Bundle savedInstanceState) {
mApiApp = apiDataAccessObject.getApiApp(packageName);
// get application name and icon from package manager
String appName;
Drawable appIcon = null;
PackageManager pm = getApplicationContext().getPackageManager();
try {
ApplicationInfo ai = pm.getApplicationInfo(mAppSettings.getPackageName(), 0);
ApplicationInfo ai = pm.getApplicationInfo(mApiApp.package_name(), 0);
appName = (String) pm.getApplicationLabel(ai);
appIcon = pm.getApplicationIcon(ai);
} catch (PackageManager.NameNotFoundException e) {
// fallback
appName = mAppSettings.getPackageName();
appName = mApiApp.package_name();
}
mAppNameView.setText(appName);
mAppIconView.setImageDrawable(appIcon);
Uri allowedKeysUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ALLOWED_KEYS).build();
Timber.d("allowedKeysUri: " + allowedKeysUri);
startListFragments(savedInstanceState, allowedKeysUri);
startListFragments(savedInstanceState);
}
private void startListFragments(Bundle savedInstanceState, Uri allowedKeysUri) {
private void startListFragments(Bundle savedInstanceState) {
// 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.
@@ -199,7 +199,8 @@ public class AppSettingsActivity extends BaseActivity {
return;
}
AppSettingsAllowedKeysListFragment allowedKeysFragment = AppSettingsAllowedKeysListFragment.newInstance(allowedKeysUri);
// Create an instance of the fragments
AppSettingsAllowedKeysListFragment allowedKeysFragment = AppSettingsAllowedKeysListFragment.newInstance(packageName);
// Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
getSupportFragmentManager().beginTransaction()
@@ -210,9 +211,7 @@ public class AppSettingsActivity extends BaseActivity {
}
private void revokeAccess() {
if (getContentResolver().delete(mAppUri, null, null) <= 0) {
throw new RuntimeException();
}
apiDataAccessObject.deleteApiApp(packageName);
finish();
}

View File

@@ -20,11 +20,9 @@ package org.sufficientlysecure.keychain.remote.ui;
import java.util.Set;
import android.content.OperationApplicationException;
import android.database.Cursor;
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;
@@ -40,25 +38,24 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
import org.sufficientlysecure.keychain.ui.adapter.KeySelectableAdapter;
import org.sufficientlysecure.keychain.ui.widget.FixedListView;
import timber.log.Timber;
public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String ARG_DATA_URI = "uri";
private static final String ARG_PACKAGE_NAME = "package_name";
private KeySelectableAdapter mAdapter;
private ApiDataAccessObject mApiDao;
private Uri mDataUri;
private String packageName;
/**
* Creates new instance of this fragment
*/
public static AppSettingsAllowedKeysListFragment newInstance(Uri dataUri) {
public static AppSettingsAllowedKeysListFragment newInstance(String packageName) {
AppSettingsAllowedKeysListFragment frag = new AppSettingsAllowedKeysListFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_DATA_URI, dataUri);
args.putString(ARG_PACKAGE_NAME, packageName);
frag.setArguments(args);
@@ -101,13 +98,13 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mDataUri = getArguments().getParcelable(ARG_DATA_URI);
packageName = getArguments().getString(ARG_PACKAGE_NAME);
// 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));
Set<Long> checked = mApiDao.getAllowedKeyIdsForApp(mDataUri);
Set<Long> checked = mApiDao.getAllowedKeyIdsForApp(packageName);
mAdapter = new KeySelectableAdapter(getActivity(), null, 0, checked);
setListAdapter(mAdapter);
getListView().setOnItemClickListener(mAdapter);
@@ -140,11 +137,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
} */
public void saveAllowedKeys() {
try {
mApiDao.saveAllowedKeyIdsForApp(mDataUri, getSelectedMasterKeyIds());
} catch (RemoteException | OperationApplicationException e) {
Timber.e(e, "Problem saving allowed key ids!");
}
mApiDao.saveAllowedKeyIdsForApp(packageName, getSelectedMasterKeyIds());
}
@Override

View File

@@ -1,107 +0,0 @@
/*
* Copyright (C) 2017 Schürmann & Breitmoser GbR
*
* 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.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.remote.AppSettings;
import timber.log.Timber;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class AppSettingsHeaderFragment extends Fragment {
// model
private AppSettings mAppSettings;
// view
private TextView mAppNameView;
private ImageView mAppIconView;
private TextView mPackageName;
private TextView mPackageCertificate;
public AppSettings getAppSettings() {
return mAppSettings;
}
public void setAppSettings(AppSettings appSettings) {
this.mAppSettings = appSettings;
updateView(appSettings);
}
/**
* Inflate the layout for this fragment
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
mAppNameView = view.findViewById(R.id.api_app_settings_app_name);
mAppIconView = view.findViewById(R.id.api_app_settings_app_icon);
mPackageName = view.findViewById(R.id.api_app_settings_package_name);
mPackageCertificate = view.findViewById(R.id.api_app_settings_package_certificate);
return view;
}
private void updateView(AppSettings appSettings) {
// get application name and icon from package manager
String appName;
Drawable appIcon = null;
PackageManager pm = getActivity().getApplicationContext().getPackageManager();
try {
ApplicationInfo ai = pm.getApplicationInfo(appSettings.getPackageName(), 0);
appName = (String) pm.getApplicationLabel(ai);
appIcon = pm.getApplicationIcon(ai);
} catch (NameNotFoundException e) {
// fallback
appName = appSettings.getPackageName();
}
mAppNameView.setText(appName);
mAppIconView.setImageDrawable(appIcon);
// advanced info: package name
mPackageName.setText(appSettings.getPackageName());
// advanced info: package signature SHA-256
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(appSettings.getPackageCertificate());
byte[] digest = md.digest();
String signature = new String(Hex.encode(digest));
mPackageCertificate.setText(signature);
} catch (NoSuchAlgorithmException e) {
Timber.e(e, "Should not happen!");
}
}
}

View File

@@ -17,87 +17,77 @@
package org.sufficientlysecure.keychain.remote.ui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.CursorJoiner;
import android.database.MatrixCursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
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.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter;
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.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.model.ApiApp;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.remote.ui.AppsListFragment.ApiAppAdapter;
import org.sufficientlysecure.keychain.ui.base.RecyclerFragment;
import org.sufficientlysecure.keychain.ui.keyview.loader.AsyncTaskLiveData;
import timber.log.Timber;
public class AppsListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener {
AppsAdapter mAdapter;
public class AppsListFragment extends RecyclerFragment<ApiAppAdapter> {
private ApiAppAdapter adapter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setOnItemClickListener(this);
// NOTE: No setEmptyText(), we always have the default entries
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new AppsAdapter(getActivity(), null, 0);
setListAdapter(mAdapter);
adapter = new ApiAppAdapter(getActivity());
setAdapter(adapter);
setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
// NOTE: Loader is started in onResume!
new ApiAppsLiveData(getContext()).observe(this, this::onLoad);
}
@Override
public void onResume() {
super.onResume();
// Start out with a progress indicator.
setListShown(false);
// After coming back from Google Play -> reload
getLoaderManager().restartLoader(0, null, this);
private void onLoad(List<ListedApp> apiApps) {
if (apiApps == null) {
hideList(false);
adapter.setData(null);
return;
}
adapter.setData(apiApps);
showList(true);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String selectedPackageName = mAdapter.getItemPackageName(position);
boolean installed = mAdapter.getItemIsInstalled(position);
boolean registered = mAdapter.getItemIsRegistered(position);
public void onItemClick(int position) {
ListedApp listedApp = adapter.data.get(position);
if (installed) {
if (registered) {
if (listedApp.isInstalled) {
if (listedApp.isRegistered) {
// Edit app settings
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName));
intent.putExtra(AppSettingsActivity.EXTRA_PACKAGE_NAME, listedApp.packageName);
startActivity(intent);
} else {
Intent i;
PackageManager manager = getActivity().getPackageManager();
try {
i = manager.getLaunchIntentForPackage(selectedPackageName);
i = manager.getLaunchIntentForPackage(listedApp.packageName);
if (i == null) {
throw new PackageManager.NameNotFoundException();
}
@@ -112,256 +102,163 @@ public class AppsListFragment extends ListFragment implements
} else {
try {
startActivity(new Intent(Intent.ACTION_VIEW,
Uri.parse("market://details?id=" + selectedPackageName)));
Uri.parse("market://details?id=" + listedApp.packageName)));
} catch (ActivityNotFoundException anfe) {
startActivity(new Intent(Intent.ACTION_VIEW,
Uri.parse("https://play.google.com/store/apps/details?id=" + selectedPackageName)));
Uri.parse("https://play.google.com/store/apps/details?id=" + listedApp.packageName)));
}
}
}
private static final String TEMP_COLUMN_NAME = "NAME";
private static final String TEMP_COLUMN_INSTALLED = "INSTALLED";
private static final String TEMP_COLUMN_REGISTERED = "REGISTERED";
private static final String TEMP_COLUMN_ICON_RES_ID = "ICON_RES_ID";
public class ApiAppAdapter extends Adapter<ApiAppViewHolder> {
private final LayoutInflater inflater;
static final String[] PROJECTION = new String[]{
ApiApps._ID, // 0
ApiApps.PACKAGE_NAME, // 1
"null as " + TEMP_COLUMN_NAME, // installed apps can retrieve app name from Android OS
"0 as " + TEMP_COLUMN_INSTALLED, // changed later in cursor joiner
"1 as " + TEMP_COLUMN_REGISTERED, // if it is in db it is registered
"0 as " + TEMP_COLUMN_ICON_RES_ID // not used
private List<ListedApp> data;
ApiAppAdapter(Context context) {
super();
inflater = LayoutInflater.from(context);
}
@Override
public ApiAppViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ApiAppViewHolder(inflater.inflate(R.layout.api_apps_adapter_list_item, parent, false));
}
@Override
public void onBindViewHolder(ApiAppViewHolder holder, int position) {
ListedApp item = data.get(position);
holder.bind(item);
}
@Override
public int getItemCount() {
return data != null ? data.size() : 0;
}
public void setData(List<ListedApp> data) {
this.data = data;
notifyDataSetChanged();
}
}
public class ApiAppViewHolder extends RecyclerView.ViewHolder {
private final TextView text;
private final ImageView icon;
private final ImageView installIcon;
ApiAppViewHolder(View itemView) {
super(itemView);
text = itemView.findViewById(R.id.api_apps_adapter_item_name);
icon = itemView.findViewById(R.id.api_apps_adapter_item_icon);
installIcon = itemView.findViewById(R.id.api_apps_adapter_install_icon);
itemView.setOnClickListener((View view) -> onItemClick(getAdapterPosition()));
}
void bind(ListedApp listedApp) {
text.setText(listedApp.readableName);
if (listedApp.applicationIconRes != null) {
icon.setImageResource(listedApp.applicationIconRes);
} else {
icon.setImageDrawable(listedApp.applicationIcon);
}
installIcon.setVisibility(listedApp.isInstalled ? View.GONE : View.VISIBLE);
}
}
public static class ApiAppsLiveData extends AsyncTaskLiveData<List<ListedApp>> {
private final ApiDataAccessObject apiDao;
private final PackageManager packageManager;
ApiAppsLiveData(Context context) {
super(context, null);
packageManager = getContext().getPackageManager();
apiDao = new ApiDataAccessObject(context);
}
@Override
protected List<ListedApp> asyncLoadData() {
ArrayList<ListedApp> result = new ArrayList<>();
loadRegisteredApps(result);
addPlaceholderApps(result);
Collections.sort(result, (o1, o2) -> o1.readableName.compareTo(o2.readableName));
return result;
}
private void loadRegisteredApps(ArrayList<ListedApp> result) {
List<ApiApp> registeredApiApps = apiDao.getAllApiApps();
for (ApiApp apiApp : registeredApiApps) {
ListedApp listedApp;
try {
ApplicationInfo ai = packageManager.getApplicationInfo(apiApp.package_name(), 0);
CharSequence applicationLabel = packageManager.getApplicationLabel(ai);
Drawable applicationIcon = packageManager.getApplicationIcon(ai);
listedApp = new ListedApp(apiApp.package_name(), true, true, applicationLabel, applicationIcon, null);
} catch (PackageManager.NameNotFoundException e) {
listedApp = new ListedApp(apiApp.package_name(), false, true, apiApp.package_name(), null, null);
}
result.add(listedApp);
}
}
private void addPlaceholderApps(ArrayList<ListedApp> result) {
for (ListedApp placeholderApp : PLACERHOLDER_APPS) {
if (!containsByPackageName(result, placeholderApp.packageName)) {
try {
packageManager.getApplicationInfo(placeholderApp.packageName, 0);
result.add(placeholderApp.withIsInstalled());
} catch (PackageManager.NameNotFoundException e) {
result.add(placeholderApp);
}
}
}
}
private boolean containsByPackageName(ArrayList<ListedApp> result, String packageName) {
for (ListedApp app : result) {
if (packageName.equals(app.packageName)) {
return true;
}
}
return false;
}
}
public static class ListedApp {
final String packageName;
final boolean isInstalled;
final boolean isRegistered;
final String readableName;
final Drawable applicationIcon;
final Integer applicationIconRes;
ListedApp(String packageName, boolean isInstalled, boolean isRegistered, CharSequence readableName,
Drawable applicationIcon, Integer applicationIconRes) {
this.packageName = packageName;
this.isInstalled = isInstalled;
this.isRegistered = isRegistered;
this.readableName = readableName.toString();
this.applicationIcon = applicationIcon;
this.applicationIconRes = applicationIconRes;
}
public ListedApp withIsInstalled() {
return new ListedApp(packageName, true, isRegistered, readableName, applicationIcon, applicationIconRes);
}
}
private static final ListedApp[] PLACERHOLDER_APPS = {
new ListedApp("com.fsck.k9", false, false, "K-9 Mail", null, R.drawable.apps_k9),
new ListedApp("com.zeapo.pwdstore", false, false, "Password Store", null, R.drawable.apps_password_store),
new ListedApp("eu.siacs.conversations", false, false, "Conversations (Instant Messaging)", null,
R.drawable.apps_conversations)
};
private static final int INDEX_ID = 0;
private static final int INDEX_PACKAGE_NAME = 1;
private static final int INDEX_NAME = 2;
private static final int INDEX_INSTALLED = 3;
private static final int INDEX_REGISTERED = 4;
private static final int INDEX_ICON_RES_ID = 5;
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// 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 = ApiApps.CONTENT_URI;
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new AppsLoader(getActivity(), baseUri, PROJECTION, null, null,
ApiApps.PACKAGE_NAME + " COLLATE LOCALIZED ASC");
}
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.
setListShown(true);
}
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);
}
/**
* Besides the queried cursor with all registered apps, this loader also returns non-installed
* proposed apps using a MatrixCursor.
*/
private static class AppsLoader extends CursorLoader {
public AppsLoader(Context context) {
super(context);
}
public AppsLoader(Context context, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
super(context, uri, projection, selection, selectionArgs, sortOrder);
}
@Override
public Cursor loadInBackground() {
// Load registered apps from content provider
Cursor data = super.loadInBackground();
MatrixCursor availableAppsCursor = new MatrixCursor(new String[]{
ApiApps._ID,
ApiApps.PACKAGE_NAME,
TEMP_COLUMN_NAME,
TEMP_COLUMN_INSTALLED,
TEMP_COLUMN_REGISTERED,
TEMP_COLUMN_ICON_RES_ID
});
// NOTE: SORT ascending by package name, this is REQUIRED for CursorJoiner!
// Drawables taken from projects res/drawables-xxhdpi/ic_launcher.png
availableAppsCursor.addRow(new Object[]{1, "com.fsck.k9", "K-9 Mail", 0, 0, R.drawable.apps_k9});
availableAppsCursor.addRow(new Object[]{1, "com.zeapo.pwdstore", "Password Store", 0, 0, R.drawable.apps_password_store});
availableAppsCursor.addRow(new Object[]{1, "eu.siacs.conversations", "Conversations (Instant Messaging)", 0, 0, R.drawable.apps_conversations});
MatrixCursor mergedCursor = new MatrixCursor(new String[]{
ApiApps._ID,
ApiApps.PACKAGE_NAME,
TEMP_COLUMN_NAME,
TEMP_COLUMN_INSTALLED,
TEMP_COLUMN_REGISTERED,
TEMP_COLUMN_ICON_RES_ID
});
CursorJoiner joiner = new CursorJoiner(
availableAppsCursor,
new String[]{ApiApps.PACKAGE_NAME},
data,
new String[]{ApiApps.PACKAGE_NAME});
for (CursorJoiner.Result joinerResult : joiner) {
switch (joinerResult) {
case LEFT: {
// handle case where a row in availableAppsCursor is unique
String packageName = availableAppsCursor.getString(INDEX_PACKAGE_NAME);
mergedCursor.addRow(new Object[]{
1, // no need for unique _ID
packageName,
availableAppsCursor.getString(INDEX_NAME),
isInstalled(packageName),
0,
availableAppsCursor.getInt(INDEX_ICON_RES_ID)
});
break;
}
case RIGHT: {
// handle case where a row in data is unique
String packageName = data.getString(INDEX_PACKAGE_NAME);
mergedCursor.addRow(new Object[]{
1, // no need for unique _ID
packageName,
null,
isInstalled(packageName),
1, // registered!
R.mipmap.ic_launcher // icon is retrieved later
});
break;
}
case BOTH: {
// handle case where a row with the same key is in both cursors
String packageName = data.getString(INDEX_PACKAGE_NAME);
String name;
if (isInstalled(packageName) == 1) {
name = data.getString(INDEX_NAME);
} else {
// if not installed take name from available apps list
name = availableAppsCursor.getString(INDEX_NAME);
}
mergedCursor.addRow(new Object[]{
1, // no need for unique _ID
packageName,
name,
isInstalled(packageName),
1, // registered!
R.mipmap.ic_launcher // icon is retrieved later
});
break;
}
}
}
return mergedCursor;
}
private int isInstalled(String packageName) {
try {
getContext().getPackageManager().getApplicationInfo(packageName, 0);
return 1;
} catch (final PackageManager.NameNotFoundException e) {
return 0;
}
}
}
private class AppsAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private PackageManager mPM;
public AppsAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
mPM = context.getApplicationContext().getPackageManager();
}
/**
* Similar to CursorAdapter.getItemId().
* Required to build Uris for api apps, which are not based on row ids
*/
public String getItemPackageName(int position) {
if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
return mCursor.getString(INDEX_PACKAGE_NAME);
} else {
return null;
}
}
public boolean getItemIsInstalled(int position) {
return mDataValid && mCursor != null
&& mCursor.moveToPosition(position) && (mCursor.getInt(INDEX_INSTALLED) == 1);
}
public boolean getItemIsRegistered(int position) {
return mDataValid && mCursor != null
&& mCursor.moveToPosition(position) && (mCursor.getInt(INDEX_REGISTERED) == 1);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView text = view.findViewById(R.id.api_apps_adapter_item_name);
ImageView icon = view.findViewById(R.id.api_apps_adapter_item_icon);
ImageView installIcon = view.findViewById(R.id.api_apps_adapter_install_icon);
String packageName = cursor.getString(INDEX_PACKAGE_NAME);
Timber.d("packageName: " + packageName);
int installed = cursor.getInt(INDEX_INSTALLED);
String name = cursor.getString(INDEX_NAME);
int iconResName = cursor.getInt(INDEX_ICON_RES_ID);
// get application name and icon
try {
ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0);
text.setText(mPM.getApplicationLabel(ai));
icon.setImageDrawable(mPM.getApplicationIcon(ai));
} catch (final PackageManager.NameNotFoundException e) {
// fallback
if (name == null) {
text.setText(packageName);
} else {
text.setText(name);
try {
icon.setImageDrawable(getResources().getDrawable(iconResName));
} catch (Resources.NotFoundException e1) {
// silently fail
}
}
}
if (installed == 1) {
installIcon.setVisibility(View.GONE);
} else {
installIcon.setVisibility(View.VISIBLE);
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
}
}
}

View File

@@ -26,8 +26,8 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.model.ApiApp;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.remote.AppSettings;
import timber.log.Timber;
@@ -39,7 +39,7 @@ class RemoteRegisterPresenter {
private RemoteRegisterView view;
private Intent resultData;
private AppSettings appSettings;
private ApiApp apiApp;
RemoteRegisterPresenter(Context context) {
@@ -54,7 +54,7 @@ class RemoteRegisterPresenter {
}
void setupFromIntentData(Intent resultData, String packageName, byte[] packageSignature) {
this.appSettings = new AppSettings(packageName, packageSignature);
this.apiApp = ApiApp.create(packageName, packageSignature);
this.resultData = resultData;
try {
@@ -76,7 +76,7 @@ class RemoteRegisterPresenter {
}
void onClickAllow() {
apiDao.insertApiApp(appSettings);
apiDao.insertApiApp(apiApp);
view.finishWithResult(resultData);
}

View File

@@ -34,6 +34,7 @@ import timber.log.Timber;
public class SelectSignKeyIdActivity extends BaseActivity {
public static final String EXTRA_PACKAGE_NAME = "package_name";
public static final String EXTRA_USER_ID = OpenPgpApi.EXTRA_USER_ID;
public static final String EXTRA_DATA = "data";
@@ -68,19 +69,18 @@ public class SelectSignKeyIdActivity extends BaseActivity {
});
Intent intent = getIntent();
Uri appUri = intent.getData();
String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
mPreferredUserId = intent.getStringExtra(EXTRA_USER_ID);
mData = intent.getParcelableExtra(EXTRA_DATA);
if (appUri == null) {
if (packageName == null) {
Timber.e("Intent data missing. Should be Uri of app!");
finish();
} else {
Timber.d("uri: " + appUri);
startListFragments(savedInstanceState, appUri, mData, mPreferredUserId);
startListFragments(savedInstanceState, packageName, mData, mPreferredUserId);
}
}
private void startListFragments(Bundle savedInstanceState, Uri dataUri, Intent data, String preferredUserId) {
private void startListFragments(Bundle savedInstanceState, String packageName, Intent data, String preferredUserId) {
// 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.
@@ -90,7 +90,7 @@ public class SelectSignKeyIdActivity extends BaseActivity {
// Create an instance of the fragments
SelectSignKeyIdListFragment listFragment = SelectSignKeyIdListFragment
.newInstance(dataUri, data, preferredUserId);
.newInstance(packageName, data, preferredUserId);
// Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
getSupportFragmentManager().beginTransaction()

View File

@@ -33,35 +33,33 @@ import org.openintents.openpgp.util.OpenPgpUtils;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.remote.ui.adapter.SelectSignKeyAdapter;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.util.adapter.CursorAdapter;
import org.sufficientlysecure.keychain.ui.base.RecyclerFragment;
import timber.log.Timber;
import org.sufficientlysecure.keychain.ui.util.adapter.CursorAdapter;
public class SelectSignKeyIdListFragment extends RecyclerFragment<SelectSignKeyAdapter>
implements SelectSignKeyAdapter.SelectSignKeyListener, LoaderManager.LoaderCallbacks<Cursor> {
private static final String ARG_DATA_URI = "uri";
private static final String ARG_PACKAGE_NAME = "package_name";
private static final String ARG_PREF_UID = "pref_uid";
public static final String ARG_DATA = "data";
private Uri mDataUri;
private Intent mResult;
private String mPrefUid;
private ApiDataAccessObject mApiDao;
private String mPackageName;
/**
* Creates new instance of this fragment
*/
public static SelectSignKeyIdListFragment newInstance(Uri dataUri, Intent data, String preferredUserId) {
public static SelectSignKeyIdListFragment newInstance(String packageName, Intent data, String preferredUserId) {
SelectSignKeyIdListFragment frag = new SelectSignKeyIdListFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_DATA_URI, dataUri);
args.putString(ARG_PACKAGE_NAME, packageName);
args.putParcelable(ARG_DATA, data);
args.putString(ARG_PREF_UID, preferredUserId);
@@ -85,7 +83,7 @@ public class SelectSignKeyIdListFragment extends RecyclerFragment<SelectSignKeyA
mResult = getArguments().getParcelable(ARG_DATA);
mPrefUid = getArguments().getString(ARG_PREF_UID);
mDataUri = getArguments().getParcelable(ARG_DATA_URI);
mPackageName = getArguments().getString(ARG_PACKAGE_NAME);
// Give some text to display if there is no data. In a real
// application this would come from a resource.
@@ -175,16 +173,9 @@ public class SelectSignKeyIdListFragment extends RecyclerFragment<SelectSignKeyA
@Override
public void onSelectKeyItemClicked(long masterKeyId) {
Uri allowedKeysUri = mDataUri.buildUpon()
.appendPath(KeychainContract.PATH_ALLOWED_KEYS)
.build();
mApiDao.addAllowedKeyIdForApp(allowedKeysUri, masterKeyId);
mApiDao.addAllowedKeyIdForApp(mPackageName, masterKeyId);
mResult.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, masterKeyId);
Timber.d("allowedKeyId: " + masterKeyId);
Timber.d("allowedKeysUri: " + allowedKeysUri);
getActivity().setResult(Activity.RESULT_OK, mResult);
getActivity().finish();
}

View File

@@ -18,6 +18,8 @@
package org.sufficientlysecure.keychain.remote.ui.dialog;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
@@ -27,7 +29,6 @@ import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
@@ -48,21 +49,17 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.mikepenz.materialdrawer.util.KeyboardUtil;
import org.openintents.ssh.authentication.SshAuthenticationApi;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.remote.ui.RemoteSecurityTokenOperationActivity;
import org.sufficientlysecure.keychain.livedata.KeyInfoInteractor.KeyInfo;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.remote.ui.RemoteSecurityTokenOperationActivity;
import org.sufficientlysecure.keychain.remote.ui.dialog.RemoteSelectAuthenticationKeyPresenter.RemoteSelectAuthenticationKeyView;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
import org.sufficientlysecure.keychain.ui.util.recyclerview.DividerItemDecoration;
import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerItemClickListener;
import java.util.List;
public class RemoteSelectAuthenticationKeyActivity extends FragmentActivity {
public static final String EXTRA_PACKAGE_NAME = "package_name";
@@ -71,6 +68,7 @@ public class RemoteSelectAuthenticationKeyActivity extends FragmentActivity {
private RemoteSelectAuthenticationKeyPresenter presenter;
private String packageName;
@Override
@@ -92,8 +90,7 @@ public class RemoteSelectAuthenticationKeyActivity extends FragmentActivity {
super.onStart();
Intent intent = getIntent();
String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
presenter.setupFromIntentData(packageName);
presenter.startLoaders(getSupportLoaderManager());
@@ -104,14 +101,8 @@ public class RemoteSelectAuthenticationKeyActivity extends FragmentActivity {
Intent originalIntent = callingIntent.getParcelableExtra(
RemoteSecurityTokenOperationActivity.EXTRA_DATA);
Uri appUri = callingIntent.getData();
Uri allowedKeysUri = appUri.buildUpon()
.appendPath(KeychainContract.PATH_ALLOWED_KEYS)
.build();
ApiDataAccessObject apiDao = new ApiDataAccessObject(getBaseContext());
apiDao.addAllowedKeyIdForApp(allowedKeysUri, masterKeyId);
apiDao.addAllowedKeyIdForApp(packageName, masterKeyId);
originalIntent.putExtra(SshAuthenticationApi.EXTRA_KEY_ID, String.valueOf(masterKeyId));

View File

@@ -34,12 +34,12 @@ import org.openintents.openpgp.util.OpenPgpUtils.UserId;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.livedata.KeyInfoInteractor.KeyInfo;
import org.sufficientlysecure.keychain.livedata.KeyInfoInteractor.KeySelector;
import org.sufficientlysecure.keychain.model.ApiApp;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import timber.log.Timber;
@@ -58,7 +58,7 @@ class RemoteSelectIdentityKeyPresenter {
private long selectedMasterKeyId;
private byte[] generatedKeyData;
private ApiDataAccessObject apiDao;
private AppSettings appSettings;
private ApiApp apiApp;
RemoteSelectIdentityKeyPresenter(Context context, RemoteSelectIdViewModel viewModel, LifecycleOwner lifecycleOwner) {
@@ -103,7 +103,7 @@ class RemoteSelectIdentityKeyPresenter {
Drawable appIcon = packageManager.getApplicationIcon(applicationInfo);
CharSequence appLabel = packageManager.getApplicationLabel(applicationInfo);
appSettings = new AppSettings(packageName, packageSignature);
apiApp = ApiApp.create(packageName, packageSignature);
view.setTitleClientIconAndName(appIcon, appLabel);
}
@@ -200,15 +200,15 @@ class RemoteSelectIdentityKeyPresenter {
}
void onHighlightFinished() {
apiDao.insertApiApp(appSettings);
apiDao.addAllowedKeyIdForApp(appSettings.getPackageName(), selectedMasterKeyId);
apiDao.insertApiApp(apiApp);
apiDao.addAllowedKeyIdForApp(apiApp.package_name(), selectedMasterKeyId);
view.finishAndReturn(selectedMasterKeyId);
}
void onImportOpSuccess(ImportKeyResult result) {
long importedMasterKeyId = result.getImportedMasterKeyIds()[0];
apiDao.insertApiApp(appSettings);
apiDao.addAllowedKeyIdForApp(appSettings.getPackageName(), selectedMasterKeyId);
apiDao.insertApiApp(apiApp);
apiDao.addAllowedKeyIdForApp(apiApp.package_name(), selectedMasterKeyId);
view.finishAndReturn(importedMasterKeyId);
}