diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceTest.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceTest.java index d12fc6333..1e84ea879 100644 --- a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceTest.java +++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceTest.java @@ -82,7 +82,7 @@ public class OpenPgpServiceTest { pi.send(); Thread.sleep(ACTIVITY_WAIT_TIME); // Wait for activity to start - onView(withText(R.string.api_register_allow)).perform(click()); + onView(withText(R.string.button_allow)).perform(click()); } byte[] ciphertext; @@ -121,10 +121,7 @@ public class OpenPgpServiceTest { pi.send(); Thread.sleep(ACTIVITY_WAIT_TIME); // Wait for activity to start - onData(withKeyItemId(0x9D604D2F310716A3L)) - .inAdapterView(isAssignableFrom(AdapterView.class)) - .perform(click()); - onView(withText(R.string.api_settings_save)).perform(click()); + onView(withText(R.string.button_allow)).perform(click()); } { // decrypt again, this time pending passphrase diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index bad087468..f98a7f3e3 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -861,6 +861,7 @@ CREATOR = new Creator() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index e3b3139a4..88334e9af 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -29,6 +29,7 @@ import java.io.OutputStream; import java.nio.ByteBuffer; import java.security.SignatureException; import java.util.Date; +import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -208,7 +209,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation skippedDisallowedEncryptionKeys = new HashSet<>(); boolean insecureEncryptionKey = false; // convenience method to return with error @@ -607,7 +608,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation 0) { // allow user to select allowed keys Intent result = new Intent(); String packageName = mApiPermissionHelper.getCurrentCallingPackage(); result.putExtra(OpenPgpApi.RESULT_INTENT, - mApiPendingIntentFactory.createSelectAllowedKeysPendingIntent(data, packageName)); + mApiPendingIntentFactory.createRequestKeyPermissionPendingIntent( + data, packageName, skippedDisallowedEncryptionKeys)); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); return result; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractor.java index e869a2e25..3725df272 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractor.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractor.java @@ -2,7 +2,6 @@ package org.sufficientlysecure.keychain.remote; import java.util.ArrayList; -import java.util.Collection; import java.util.HashSet; import android.app.PendingIntent; @@ -10,7 +9,6 @@ import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; import android.net.Uri; -import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; import org.openintents.openpgp.OpenPgpError; @@ -21,6 +19,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; @@ -146,7 +145,7 @@ class OpenPgpServiceKeyIdExtractor { } if (!hasUserIds || hasMissingUserIds || hasDuplicateUserIds) { - long[] keyIdsArray = getUnboxedLongArray(keyIds); + long[] keyIdsArray = KeyFormattingUtils.getUnboxedLongArray(keyIds); PendingIntent pi = apiPendingIntentFactory.createSelectPublicKeyPendingIntent(data, keyIdsArray, missingEmails, duplicateEmails, hasUserIds); @@ -164,16 +163,6 @@ class OpenPgpServiceKeyIdExtractor { return new KeyIdResult(keyIds); } - @NonNull - private static long[] getUnboxedLongArray(@NonNull Collection arrayList) { - long[] result = new long[arrayList.size()]; - int i = 0; - for (Long e : arrayList) { - result[i++] = e; - } - return result; - } - static class KeyIdResult { private final Intent mResultIntent; private final HashSet mKeyIds; @@ -206,7 +195,7 @@ class OpenPgpServiceKeyIdExtractor { if (mKeyIds == null) { throw new AssertionError("key ids must not be null when getKeyIds is called!"); } - return getUnboxedLongArray(mKeyIds); + return KeyFormattingUtils.getUnboxedLongArray(mKeyIds); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterActivity.java index 0ce9e66c9..567757c44 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterActivity.java @@ -18,73 +18,173 @@ package org.sufficientlysecure.keychain.remote.ui; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Dialog; +import android.content.DialogInterface; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.ApiDataAccessObject; -import org.sufficientlysecure.keychain.remote.AppSettings; -import org.sufficientlysecure.keychain.ui.base.BaseActivity; -import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.remote.ui.RemoteRegisterPresenter.RemoteRegisterView; +import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; +import org.sufficientlysecure.keychain.ui.util.ThemeChanger; -public class RemoteRegisterActivity extends BaseActivity { +public class RemoteRegisterActivity extends FragmentActivity { public static final String EXTRA_PACKAGE_NAME = "package_name"; public static final String EXTRA_PACKAGE_SIGNATURE = "package_signature"; - public static final String EXTRA_DATA = "data"; - private AppSettingsHeaderFragment mAppSettingsHeaderFragment; - @Override - protected void initLayout() { - setContentView(R.layout.api_remote_register_app); - } + private RemoteRegisterPresenter presenter; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final Bundle extras = getIntent().getExtras(); + this.presenter = new RemoteRegisterPresenter(getBaseContext()); - final String packageName = extras.getString(EXTRA_PACKAGE_NAME); - final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE); - Log.d(Constants.TAG, "ACTION_REGISTER packageName: " + packageName); + if (savedInstanceState == null) { + RemoteRegisterDialogFragment frag = new RemoteRegisterDialogFragment(); + frag.show(getSupportFragmentManager(), "requestKeyDialog"); + } + } - final ApiDataAccessObject apiDao = new ApiDataAccessObject(this); + @Override + protected void onStart() { + super.onStart(); - mAppSettingsHeaderFragment = (AppSettingsHeaderFragment) getSupportFragmentManager().findFragmentById( - R.id.api_app_settings_fragment); + Intent intent = getIntent(); + Intent resultData = intent.getParcelableExtra(EXTRA_DATA); + String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); + byte[] packageSignature = intent.getByteArrayExtra(EXTRA_PACKAGE_SIGNATURE); - AppSettings settings = new AppSettings(packageName, packageSignature); - mAppSettingsHeaderFragment.setAppSettings(settings); + presenter.setupFromIntentData(resultData, packageName, packageSignature); + } - // Inflate a "Done"/"Cancel" custom action bar view - setFullScreenDialogTwoButtons( - R.string.api_register_allow, R.drawable.ic_check_white_24dp, - new View.OnClickListener() { - @Override - public void onClick(View v) { - // Allow - apiDao.insertApiApp(mAppSettingsHeaderFragment.getAppSettings()); + public static class RemoteRegisterDialogFragment extends DialogFragment { + private RemoteRegisterPresenter presenter; + private RemoteRegisterView mvpView; - // give data through for new service call - Intent resultData = extras.getParcelable(EXTRA_DATA); - RemoteRegisterActivity.this.setResult(RESULT_OK, resultData); - RemoteRegisterActivity.this.finish(); - } - }, R.string.api_register_disallow, R.drawable.ic_close_white_24dp, - new View.OnClickListener() { - @Override - public void onClick(View v) { - // Disallow - RemoteRegisterActivity.this.setResult(RESULT_CANCELED); - RemoteRegisterActivity.this.finish(); + private Button buttonAllow; + private Button buttonCancel; + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity); + CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(theme); + + @SuppressLint("InflateParams") + View view = LayoutInflater.from(theme).inflate(R.layout.api_remote_register_app, null, false); + alert.setView(view); + + buttonAllow = (Button) view.findViewById(R.id.button_allow); + buttonCancel = (Button) view.findViewById(R.id.button_cancel); + + setupListenersForPresenter(); + mvpView = createMvpView(view); + + return alert.create(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + presenter = ((RemoteRegisterActivity) getActivity()).presenter; + presenter.setView(mvpView); + } + + @Override + public void onCancel(DialogInterface dialog) { + super.onCancel(dialog); + + if (presenter != null) { + presenter.onCancel(); + } + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + + if (presenter != null) { + presenter.setView(null); + presenter = null; + } + } + + @NonNull + private RemoteRegisterView createMvpView(View view) { + final TextView titleText = (TextView) view.findViewById(R.id.api_register_text); + final ImageView iconClientApp = (ImageView) view.findViewById(R.id.icon_client_app); + + return new RemoteRegisterView() { + @Override + public void finishWithResult(Intent resultIntent) { + FragmentActivity activity = getActivity(); + if (activity == null) { + return; } + + activity.setResult(RESULT_OK, resultIntent); + activity.finish(); } - ); + + @Override + public void finishAsCancelled() { + FragmentActivity activity = getActivity(); + if (activity == null) { + return; + } + + activity.setResult(RESULT_CANCELED); + activity.finish(); + } + + @Override + public void setTitleText(String text) { + titleText.setText(text); + } + + @Override + public void setTitleClientIcon(Drawable drawable) { + iconClientApp.setImageDrawable(drawable); + } + }; + } + + private void setupListenersForPresenter() { + buttonAllow.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + presenter.onClickAllow(); + } + }); + + buttonCancel.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + presenter.onClickCancel(); + } + }); + } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterPresenter.java new file mode 100644 index 000000000..69c80446a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteRegisterPresenter.java @@ -0,0 +1,83 @@ +package org.sufficientlysecure.keychain.remote.ui; + + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.drawable.Drawable; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.ApiDataAccessObject; +import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.util.Log; + + +class RemoteRegisterPresenter { + private final ApiDataAccessObject apiDao; + private final PackageManager packageManager; + private final Context context; + + + private RemoteRegisterView view; + private Intent resultData; + private AppSettings appSettings; + + + RemoteRegisterPresenter(Context context) { + this.context = context; + + apiDao = new ApiDataAccessObject(context); + packageManager = context.getPackageManager(); + } + + public void setView(RemoteRegisterView view) { + this.view = view; + } + + void setupFromIntentData(Intent resultData, String packageName, byte[] packageSignature) { + this.appSettings = new AppSettings(packageName, packageSignature); + this.resultData = resultData; + + try { + setPackageInfo(packageName); + } catch (NameNotFoundException e) { + Log.e(Constants.TAG, "Unable to find info of calling app!"); + view.finishAsCancelled(); + return; + } + } + + private void setPackageInfo(String packageName) throws NameNotFoundException { + ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0); + Drawable appIcon = packageManager.getApplicationIcon(applicationInfo); + CharSequence appName = packageManager.getApplicationLabel(applicationInfo); + + view.setTitleClientIcon(appIcon); + view.setTitleText(context.getString(R.string.api_register_text, appName)); + } + + void onClickAllow() { + apiDao.insertApiApp(appSettings); + view.finishWithResult(resultData); + } + + void onClickCancel() { + view.finishAsCancelled(); + } + + void onCancel() { + view.finishAsCancelled(); + } + + interface RemoteRegisterView { + void finishWithResult(Intent resultData); + void finishAsCancelled(); + + void setTitleText(String text); + void setTitleClientIcon(Drawable drawable); + } +} + diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionActivity.java new file mode 100644 index 000000000..0296e9b48 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionActivity.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2017 Vincent Breitmoser + * + * 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 . + */ + +package org.sufficientlysecure.keychain.remote.ui; + + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import org.openintents.openpgp.util.OpenPgpUtils.UserId; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.remote.ui.RequestKeyPermissionPresenter.RequestKeyPermissionMvpView; +import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; +import org.sufficientlysecure.keychain.ui.util.ThemeChanger; + + +public class RequestKeyPermissionActivity extends FragmentActivity { + public static final String EXTRA_PACKAGE_NAME = "package_name"; + public static final String EXTRA_REQUESTED_KEY_IDS = "requested_key_ids"; + + + private RequestKeyPermissionPresenter presenter; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + presenter = RequestKeyPermissionPresenter.createRequestKeyPermissionPresenter(getBaseContext()); + + if (savedInstanceState == null) { + RequestKeyPermissionFragment frag = new RequestKeyPermissionFragment(); + frag.show(getSupportFragmentManager(), "requestKeyDialog"); + } + } + + @Override + protected void onStart() { + super.onStart(); + + Intent intent = getIntent(); + String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); + long masterKeyIds[] = intent.getLongArrayExtra(EXTRA_REQUESTED_KEY_IDS); + + presenter.setupFromIntentData(packageName, masterKeyIds); + } + + public static class RequestKeyPermissionFragment extends DialogFragment { + private RequestKeyPermissionMvpView mvpView; + private RequestKeyPermissionPresenter presenter; + private Button buttonCancel; + private Button buttonAllow; + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity); + CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(theme); + + @SuppressLint("InflateParams") + View view = LayoutInflater.from(theme).inflate(R.layout.api_remote_request_key_permission, null, false); + alert.setView(view); + + buttonAllow = (Button) view.findViewById(R.id.button_allow); + buttonCancel = (Button) view.findViewById(R.id.button_cancel); + + setupListenersForPresenter(); + mvpView = createMvpView(view); + + return alert.create(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + presenter = ((RequestKeyPermissionActivity) getActivity()).presenter; + presenter.setView(mvpView); + } + + @Override + public void onCancel(DialogInterface dialog) { + super.onCancel(dialog); + + if (presenter != null) { + presenter.onCancel(); + } + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + + if (presenter != null) { + presenter.setView(null); + presenter = null; + } + } + + @NonNull + private RequestKeyPermissionMvpView createMvpView(View view) { + final TextView titleText = (TextView) view.findViewById(R.id.select_identity_key_title); + final TextView keyUserIdView = (TextView) view.findViewById(R.id.select_key_item_name); + final ImageView iconClientApp = (ImageView) view.findViewById(R.id.icon_client_app); + final View keyUnavailableWarning = view.findViewById(R.id.requested_key_unavailable_warning); + final View keyInfoLayout = view.findViewById(R.id.key_info_layout); + + return new RequestKeyPermissionMvpView() { + @Override + public void switchToLayoutRequestKeyChoice() { + keyInfoLayout.setVisibility(View.VISIBLE); + keyUnavailableWarning.setVisibility(View.GONE); + buttonAllow.setEnabled(true); + } + + @Override + public void switchToLayoutNoSecret() { + keyInfoLayout.setVisibility(View.VISIBLE); + keyUnavailableWarning.setVisibility(View.VISIBLE); + buttonAllow.setEnabled(false); + } + + @Override + public void displayKeyInfo(UserId userId) { + keyUserIdView.setText(userId.name); + } + + @Override + public void finish() { + FragmentActivity activity = getActivity(); + if (activity == null) { + return; + } + + activity.setResult(Activity.RESULT_OK); + activity.finish(); + } + + @Override + public void finishAsCancelled() { + FragmentActivity activity = getActivity(); + if (activity == null) { + return; + } + + activity.setResult(Activity.RESULT_CANCELED); + activity.finish(); + } + + @Override + public void setTitleText(String text) { + titleText.setText(text); + } + + @Override + public void setTitleClientIcon(Drawable drawable) { + iconClientApp.setImageDrawable(drawable); + } + }; + } + + private void setupListenersForPresenter() { + buttonAllow.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + presenter.onClickAllow(); + } + }); + + buttonCancel.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + presenter.onClickCancel(); + } + }); + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionPresenter.java new file mode 100644 index 000000000..b4de0fe7d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionPresenter.java @@ -0,0 +1,170 @@ +package org.sufficientlysecure.keychain.remote.ui; + + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.drawable.Drawable; +import android.support.annotation.Nullable; + +import org.openintents.openpgp.util.OpenPgpUtils.UserId; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.ApiDataAccessObject; +import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; +import org.sufficientlysecure.keychain.remote.ApiPermissionHelper; +import org.sufficientlysecure.keychain.remote.ApiPermissionHelper.WrongPackageCertificateException; +import org.sufficientlysecure.keychain.util.Log; + + +class RequestKeyPermissionPresenter { + private final Context context; + private final PackageManager packageManager; + private final ApiDataAccessObject apiDataAccessObject; + private final ApiPermissionHelper apiPermissionHelper; + + private RequestKeyPermissionMvpView view; + + private String packageName; + private long masterKeyId; + private ProviderHelper providerHelper; + + + static RequestKeyPermissionPresenter createRequestKeyPermissionPresenter(Context context) { + PackageManager packageManager = context.getPackageManager(); + ApiDataAccessObject apiDataAccessObject = new ApiDataAccessObject(context); + ApiPermissionHelper apiPermissionHelper = new ApiPermissionHelper(context, apiDataAccessObject); + ProviderHelper providerHelper = new ProviderHelper(context); + + return new RequestKeyPermissionPresenter(context, apiDataAccessObject, apiPermissionHelper, packageManager, + providerHelper); + } + + private RequestKeyPermissionPresenter(Context context, ApiDataAccessObject apiDataAccessObject, + ApiPermissionHelper apiPermissionHelper, PackageManager packageManager, ProviderHelper providerHelper) { + this.context = context; + this.apiDataAccessObject = apiDataAccessObject; + this.apiPermissionHelper = apiPermissionHelper; + this.packageManager = packageManager; + this.providerHelper = providerHelper; + } + + void setView(RequestKeyPermissionMvpView view) { + this.view = view; + } + + void setupFromIntentData(String packageName, long[] masterKeyIds) { + checkPackageAllowed(packageName); + + try { + setPackageInfo(packageName); + } catch (NameNotFoundException e) { + Log.e(Constants.TAG, "Unable to find info of calling app!"); + view.finishAsCancelled(); + return; + } + + try { + setRequestedMasterKeyId(masterKeyIds); + } catch (PgpKeyNotFoundException e) { + view.finishAsCancelled(); + } + } + + private void setRequestedMasterKeyId(long[] subKeyIds) throws PgpKeyNotFoundException { + CachedPublicKeyRing secretKeyRingOrPublicFallback = findSecretKeyRingOrPublicFallback(subKeyIds); + + if (secretKeyRingOrPublicFallback == null) { + throw new PgpKeyNotFoundException("No key found among requested!"); + } + + this.masterKeyId = secretKeyRingOrPublicFallback.getMasterKeyId(); + + UserId userId = secretKeyRingOrPublicFallback.getSplitPrimaryUserIdWithFallback(); + view.displayKeyInfo(userId); + + if (secretKeyRingOrPublicFallback.hasAnySecret()) { + view.switchToLayoutRequestKeyChoice(); + } else { + view.switchToLayoutNoSecret(); + } + } + + @Nullable + private CachedPublicKeyRing findSecretKeyRingOrPublicFallback(long[] subKeyIds) { + CachedPublicKeyRing publicFallbackRing = null; + for (long candidateSubKeyId : subKeyIds) { + try { + CachedPublicKeyRing cachedPublicKeyRing = providerHelper.getCachedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(candidateSubKeyId) + ); + + SecretKeyType secretKeyType = cachedPublicKeyRing.getSecretKeyType(candidateSubKeyId); + if (secretKeyType.isUsable()) { + return cachedPublicKeyRing; + } + if (publicFallbackRing == null) { + publicFallbackRing = cachedPublicKeyRing; + } + } catch (PgpKeyNotFoundException | NotFoundException e) { + // no matter + } + } + return publicFallbackRing; + } + + private void setPackageInfo(String packageName) throws NameNotFoundException { + this.packageName = packageName; + + ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0); + Drawable appIcon = packageManager.getApplicationIcon(applicationInfo); + CharSequence appName = packageManager.getApplicationLabel(applicationInfo); + + view.setTitleClientIcon(appIcon); + view.setTitleText(context.getString(R.string.request_permission_msg, appName)); + } + + private void checkPackageAllowed(String packageName) { + boolean packageAllowed; + try { + packageAllowed = apiPermissionHelper.isPackageAllowed(packageName); + } catch (WrongPackageCertificateException e) { + packageAllowed = false; + } + if (!packageAllowed) { + throw new IllegalStateException("Pending intent launched by unknown app!"); + } + } + + void onClickAllow() { + apiDataAccessObject.addAllowedKeyIdForApp(packageName, masterKeyId); + view.finish(); + } + + void onClickCancel() { + view.finishAsCancelled(); + } + + void onCancel() { + view.finishAsCancelled(); + } + + interface RequestKeyPermissionMvpView { + void switchToLayoutRequestKeyChoice(); + void switchToLayoutNoSecret(); + + void setTitleText(String text); + void setTitleClientIcon(Drawable drawable); + + void displayKeyInfo(UserId userId); + + void finish(); + void finishAsCancelled(); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectAllowedKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectAllowedKeysActivity.java deleted file mode 100644 index 1dc53e61a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectAllowedKeysActivity.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2015 Dominik Schürmann - * - * 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 . - */ - -package org.sufficientlysecure.keychain.remote.ui; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.ui.base.BaseActivity; -import org.sufficientlysecure.keychain.util.Log; - -public class SelectAllowedKeysActivity extends BaseActivity { - - public static final String EXTRA_SERVICE_INTENT = "data"; - - private AppSettingsAllowedKeysListFragment mAllowedKeysFragment; - - Intent mServiceData; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Inflate a "Done" custom action bar - 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) { - cancel(); - } - }); - - Intent intent = getIntent(); - mServiceData = intent.getParcelableExtra(EXTRA_SERVICE_INTENT); - Uri appUri = intent.getData(); - if (appUri == null) { - Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); - finish(); - return; - } else { - Log.d(Constants.TAG, "uri: " + appUri); - loadData(savedInstanceState, appUri); - } - } - - @Override - protected void initLayout() { - setContentView(R.layout.api_remote_select_allowed_keys); - } - - private void save() { - mAllowedKeysFragment.saveAllowedKeys(); - setResult(Activity.RESULT_OK, mServiceData); - finish(); - } - - private void cancel() { - setResult(Activity.RESULT_CANCELED); - finish(); - } - - private void loadData(Bundle savedInstanceState, Uri appUri) { - Uri allowedKeysUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ALLOWED_KEYS).build(); - Log.d(Constants.TAG, "allowedKeysUri: " + allowedKeysUri); - startListFragments(savedInstanceState, allowedKeysUri); - } - - private void startListFragments(Bundle savedInstanceState, Uri allowedKeysUri) { - // However, if we're being restored from a previous state, - // then we don't need to do anything and should return or else - // we could end up with overlapping fragments. - if (savedInstanceState != null) { - return; - } - - // Create an instance of the fragments - mAllowedKeysFragment = AppSettingsAllowedKeysListFragment.newInstance(allowedKeysUri); - // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! - getSupportFragmentManager().beginTransaction() - .replace(R.id.api_allowed_keys_list_fragment, mAllowedKeysFragment) - .commitAllowingStateLoss(); - // do it immediately! - getSupportFragmentManager().executePendingTransactions(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java index 84e130d92..27264a498 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupCodeFragment.java @@ -293,7 +293,7 @@ public class BackupCodeFragment extends CryptoOperationFragment arrayList) { + long[] result = new long[arrayList.size()]; + int i = 0; + for (Long e : arrayList) { + result[i++] = e; + } + return result; + } + public enum State { REVOKED, EXPIRED, diff --git a/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml b/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml index 407fa2d9e..dfeed3f2d 100644 --- a/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml +++ b/OpenKeychain/src/main/res/layout-w400dp/backup_code_fragment.xml @@ -354,7 +354,7 @@ - + android:layout_height="match_parent" + android:orientation="vertical" + tools:layout_marginTop="24dp" + > - - - + android:layout_height="wrap_content" + android:padding="16dp" + android:elevation="4dp" + android:background="?attr/colorPrimary" + android:gravity="center_horizontal" + tools:targetApi="lollipop"> - + + + + + + + + + + + android:overScrollMode="ifContentScrolls" + tools:ignore="UselessParent"> - - - + android:orientation="vertical" + android:paddingLeft="24dp" + android:paddingRight="24dp" + android:paddingTop="24dp" + android:paddingBottom="16dp" + > - - - \ No newline at end of file + + + + + + + + + + + + +