diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index bad087468..11e61a6a5 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -871,7 +871,7 @@
android:exported="false"
android:label="@string/app_name" />
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..8a0600159 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 skippedDisallowedKeys = new HashSet<>();
boolean insecureEncryptionKey = false;
// convenience method to return with error
@@ -607,7 +608,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation 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/RequestKeyPermissionActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionActivity.java
new file mode 100644
index 000000000..33e58a482
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionActivity.java
@@ -0,0 +1,188 @@
+/*
+ * 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.app.Activity;
+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 android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.ViewAnimator;
+
+import org.openintents.openpgp.util.OpenPgpUtils.UserId;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+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.remote.ApiPermissionHelper;
+import org.sufficientlysecure.keychain.remote.ApiPermissionHelper.WrongPackageCertificateException;
+import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
+import org.sufficientlysecure.keychain.util.Log;
+
+
+public class RequestKeyPermissionActivity extends BaseActivity {
+ public static final String EXTRA_PACKAGE_NAME = "package_name";
+ public static final String EXTRA_REQUESTED_KEY_IDS = "requested_key_ids";
+
+ private ViewAnimator viewAnimator;
+
+ private String packageName;
+ private View keyInfoLayout;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+
+ checkPackageAllowed(packageName);
+
+ // Inflate a "Done" custom action bar
+ setFullScreenDialogClose(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ });
+
+ ImageView iconClientApp = (ImageView) findViewById(R.id.icon_client_app);
+ Drawable appIcon;
+ CharSequence appName;
+ try {
+ PackageManager packageManager = getPackageManager();
+ ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
+ appIcon = packageManager.getApplicationIcon(applicationInfo);
+ appName = packageManager.getApplicationLabel(applicationInfo);
+ } catch (NameNotFoundException e) {
+ Log.e(Constants.TAG, "Unable to find info of calling app!");
+ finish();
+ return;
+ }
+ iconClientApp.setImageDrawable(appIcon);
+
+ TextView titleText = (TextView) findViewById(R.id.select_identity_key_title);
+ titleText.setText(getString(R.string.request_permission_title, appName));
+
+ viewAnimator = (ViewAnimator) findViewById(R.id.status_animator);
+ keyInfoLayout = findViewById(R.id.key_info_layout);
+
+ long[] requestedMasterKeyIds = getIntent().getLongArrayExtra(EXTRA_REQUESTED_KEY_IDS);
+ long masterKeyId = requestedMasterKeyIds[0];
+// long masterKeyId = 4817915339785265755L;
+ try {
+ CachedPublicKeyRing cachedPublicKeyRing = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId);
+
+ UserId userId = cachedPublicKeyRing.getSplitPrimaryUserIdWithFallback();
+ displayKeyInfo(masterKeyId, userId);
+
+ if (cachedPublicKeyRing.hasAnySecret()) {
+ displayRequestKeyChoice(masterKeyId);
+ } else {
+ displayNoSecretKeyError();
+ }
+ } catch (PgpKeyNotFoundException e) {
+ keyInfoLayout.setVisibility(View.GONE);
+ displayUnknownSecretKeyError();
+ }
+ }
+
+ private void displayKeyInfo(final long masterKeyId, UserId userId) {
+ keyInfoLayout.setVisibility(View.VISIBLE);
+ TextView keyUserIdView = (TextView) findViewById(R.id.select_key_item_name);
+ keyUserIdView.setText(userId.name);
+
+ findViewById(R.id.display_key).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(RequestKeyPermissionActivity.this, ViewKeyActivity.class);
+ intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
+ startActivity(intent);
+ }
+ });
+ }
+
+ private void displayRequestKeyChoice(final long masterKeyId) {
+ viewAnimator.setDisplayedChild(0);
+
+ findViewById(R.id.button_allow).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ allowAndFinish(masterKeyId);
+ }
+ });
+
+ findViewById(R.id.button_disallow).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ }
+ });
+ }
+
+ private void allowAndFinish(long masterKeyId) {
+ new ApiDataAccessObject(this).addAllowedKeyIdForApp(packageName, masterKeyId);
+
+ finish();
+ }
+
+ private void displayNoSecretKeyError() {
+ viewAnimator.setDisplayedChild(1);
+
+ }
+
+ private void displayUnknownSecretKeyError() {
+ viewAnimator.setDisplayedChild(2);
+
+ }
+
+ private void checkPackageAllowed(String packageName) {
+ ApiDataAccessObject apiDao = new ApiDataAccessObject(this);
+ ApiPermissionHelper apiPermissionHelper = new ApiPermissionHelper(this, apiDao);
+ boolean packageAllowed;
+ try {
+ packageAllowed = apiPermissionHelper.isPackageAllowed(packageName);
+ } catch (WrongPackageCertificateException e) {
+ packageAllowed = false;
+ }
+ if (!packageAllowed) {
+ throw new IllegalStateException("Pending intent launched by unknown app!");
+ }
+ }
+
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.api_remote_request_key_permission);
+ }
+
+}
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/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
index 169a0c831..b5f41796c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PorterDuff;
+import android.support.annotation.NonNull;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
@@ -51,6 +52,7 @@ import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Collection;
import java.util.Locale;
public class KeyFormattingUtils {
@@ -410,6 +412,16 @@ public class KeyFormattingUtils {
public static final int DEFAULT_COLOR = -1;
+ @NonNull
+ public static long[] getUnboxedLongArray(@NonNull Collection 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/api_remote_request_key_permission.xml b/OpenKeychain/src/main/res/layout/api_remote_request_key_permission.xml
new file mode 100644
index 000000000..ac2ccf57e
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/api_remote_request_key_permission.xml
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/api_remote_select_allowed_keys.xml b/OpenKeychain/src/main/res/layout/api_remote_select_allowed_keys.xml
deleted file mode 100644
index e052ff333..000000000
--- a/OpenKeychain/src/main/res/layout/api_remote_select_allowed_keys.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 962b83894..3e6b59a46 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -1797,4 +1797,6 @@
"Scan again"
"Close"
"Key import redirection"
+
+ %s wants to decrypt a message.
diff --git a/OpenKeychain/src/main/res/values/styles.xml b/OpenKeychain/src/main/res/values/styles.xml
index d397d8e3a..39b8cd8d7 100644
--- a/OpenKeychain/src/main/res/values/styles.xml
+++ b/OpenKeychain/src/main/res/values/styles.xml
@@ -22,6 +22,17 @@
- @color/card_view_button
+
+