aboutsummaryrefslogtreecommitdiffstats
path: root/openpgp-api
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2015-05-05 21:46:01 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2015-05-05 21:46:01 +0200
commit76ce94e1abba20232a09c8bdc497ced497b344a3 (patch)
tree188f15afd2221302c2a362c4ddfeddb17e405b1f /openpgp-api
parent87dc44e85be2638288966aaa757b369c2ce18c8e (diff)
downloadopenpgp-api-76ce94e1abba20232a09c8bdc497ced497b344a3.tar.gz
openpgp-api-76ce94e1abba20232a09c8bdc497ced497b344a3.tar.bz2
openpgp-api-76ce94e1abba20232a09c8bdc497ced497b344a3.zip
Restructure for jcenter publish
Diffstat (limited to 'openpgp-api')
-rw-r--r--openpgp-api/.gitignore33
-rw-r--r--openpgp-api/build.gradle28
-rw-r--r--openpgp-api/src/main/AndroidManifest.xml7
-rw-r--r--openpgp-api/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl24
-rw-r--r--openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpError.java118
-rw-r--r--openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpMetadata.java132
-rw-r--r--openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java182
-rw-r--r--openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpApi.java411
-rw-r--r--openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpAppPreference.java327
-rw-r--r--openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpKeyPreference.java285
-rw-r--r--openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpServiceConnection.java124
-rw-r--r--openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpUtils.java127
-rw-r--r--openpgp-api/src/main/java/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java106
-rw-r--r--openpgp-api/src/main/res/drawable-hdpi/ic_action_cancel_launchersize.pngbin0 -> 1520 bytes
-rw-r--r--openpgp-api/src/main/res/drawable-hdpi/ic_action_cancel_launchersize_light.pngbin0 -> 1940 bytes
-rw-r--r--openpgp-api/src/main/res/drawable-mdpi/ic_action_cancel_launchersize.pngbin0 -> 1032 bytes
-rw-r--r--openpgp-api/src/main/res/drawable-mdpi/ic_action_cancel_launchersize_light.pngbin0 -> 1098 bytes
-rw-r--r--openpgp-api/src/main/res/drawable-xhdpi/ic_action_cancel_launchersize.pngbin0 -> 1570 bytes
-rw-r--r--openpgp-api/src/main/res/drawable-xhdpi/ic_action_cancel_launchersize_light.pngbin0 -> 2039 bytes
-rw-r--r--openpgp-api/src/main/res/drawable-xxhdpi/ic_action_cancel_launchersize.pngbin0 -> 2345 bytes
-rw-r--r--openpgp-api/src/main/res/drawable-xxhdpi/ic_action_cancel_launchersize_light.pngbin0 -> 2404 bytes
-rw-r--r--openpgp-api/src/main/res/values-cs/strings.xml5
-rw-r--r--openpgp-api/src/main/res/values-de/strings.xml5
-rw-r--r--openpgp-api/src/main/res/values-es/strings.xml5
-rw-r--r--openpgp-api/src/main/res/values-et/strings.xml2
-rw-r--r--openpgp-api/src/main/res/values-fi/strings.xml2
-rw-r--r--openpgp-api/src/main/res/values-fr/strings.xml5
-rw-r--r--openpgp-api/src/main/res/values-is/strings.xml2
-rw-r--r--openpgp-api/src/main/res/values-it/strings.xml5
-rw-r--r--openpgp-api/src/main/res/values-ja/strings.xml5
-rw-r--r--openpgp-api/src/main/res/values-nl/strings.xml2
-rw-r--r--openpgp-api/src/main/res/values-pl/strings.xml2
-rw-r--r--openpgp-api/src/main/res/values-pt/strings.xml2
-rw-r--r--openpgp-api/src/main/res/values-ru/strings.xml5
-rw-r--r--openpgp-api/src/main/res/values-sl/strings.xml5
-rw-r--r--openpgp-api/src/main/res/values-tr/strings.xml2
-rw-r--r--openpgp-api/src/main/res/values-uk/strings.xml5
-rw-r--r--openpgp-api/src/main/res/values-zh/strings.xml2
-rw-r--r--openpgp-api/src/main/res/values/strings.xml9
39 files changed, 1974 insertions, 0 deletions
diff --git a/openpgp-api/.gitignore b/openpgp-api/.gitignore
new file mode 100644
index 0000000..a44cc0f
--- /dev/null
+++ b/openpgp-api/.gitignore
@@ -0,0 +1,33 @@
+#Android specific
+bin
+gen
+obj
+lint.xml
+local.properties
+release.properties
+ant.properties
+*.class
+*.apk
+
+#Gradle
+.gradle
+build
+gradle.properties
+
+#Maven
+target
+pom.xml.*
+
+#Eclipse
+.project
+.classpath
+.settings
+.metadata
+
+#IntelliJ IDEA
+.idea
+*.iml
+
+#Lint output
+lint-report.html
+lint-report_files/* \ No newline at end of file
diff --git a/openpgp-api/build.gradle b/openpgp-api/build.gradle
new file mode 100644
index 0000000..f2885b9
--- /dev/null
+++ b/openpgp-api/build.gradle
@@ -0,0 +1,28 @@
+apply plugin: 'com.android.library'
+apply plugin: 'bintray-release' // must be applied after your artifact generating plugin (eg. java / com.android.library)
+
+android {
+ compileSdkVersion 22
+ buildToolsVersion '21.1.2'
+
+ defaultConfig {
+ versionCode 6
+ versionName '7.0' // API-Version . minor
+ minSdkVersion 9
+ targetSdkVersion 22
+ }
+
+ // Do not abort build if lint finds errors
+ lintOptions {
+ abortOnError false
+ }
+}
+
+publish {
+ userOrg = 'sufficientlysecure'
+ groupId = 'org.sufficientlysecure'
+ artifactId = 'openpgp-api'
+ version = '7.0'
+ description = 'The OpenPGP API provides methods to execute OpenPGP operations, such as sign, encrypt, decrypt, verify, and more without user interaction from background threads'
+ website = 'https://github.com/sufficientlysecure/openpgp-api'
+} \ No newline at end of file
diff --git a/openpgp-api/src/main/AndroidManifest.xml b/openpgp-api/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f1f29cf
--- /dev/null
+++ b/openpgp-api/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.openintents.openpgp">
+
+ <application/>
+
+</manifest> \ No newline at end of file
diff --git a/openpgp-api/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl b/openpgp-api/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl
new file mode 100644
index 0000000..2451207
--- /dev/null
+++ b/openpgp-api/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp;
+
+interface IOpenPgpService {
+
+ // see OpenPgpApi for documentation
+ Intent execute(in Intent data, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
+
+} \ No newline at end of file
diff --git a/openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpError.java b/openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpError.java
new file mode 100644
index 0000000..ce8f21f
--- /dev/null
+++ b/openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpError.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parcelable versioning has been copied from Dashclock Widget
+ * https://code.google.com/p/dashclock/source/browse/api/src/main/java/com/google/android/apps/dashclock/api/ExtensionData.java
+ */
+public class OpenPgpError implements Parcelable {
+ /**
+ * Since there might be a case where new versions of the client using the library getting
+ * old versions of the protocol (and thus old versions of this class), we need a versioning
+ * system for the parcels sent between the clients and the providers.
+ */
+ public static final int PARCELABLE_VERSION = 1;
+
+ // possible values for errorId
+ public static final int CLIENT_SIDE_ERROR = -1;
+ public static final int GENERIC_ERROR = 0;
+ public static final int INCOMPATIBLE_API_VERSIONS = 1;
+ public static final int NO_OR_WRONG_PASSPHRASE = 2;
+ public static final int NO_USER_IDS = 3;
+
+ int errorId;
+ String message;
+
+ public OpenPgpError() {
+ }
+
+ public OpenPgpError(int errorId, String message) {
+ this.errorId = errorId;
+ this.message = message;
+ }
+
+ public OpenPgpError(OpenPgpError b) {
+ this.errorId = b.errorId;
+ this.message = b.message;
+ }
+
+ public int getErrorId() {
+ return errorId;
+ }
+
+ public void setErrorId(int errorId) {
+ this.errorId = errorId;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ /**
+ * NOTE: When adding fields in the process of updating this API, make sure to bump
+ * {@link #PARCELABLE_VERSION}.
+ */
+ dest.writeInt(PARCELABLE_VERSION);
+ // Inject a placeholder that will store the parcel size from this point on
+ // (not including the size itself).
+ int sizePosition = dest.dataPosition();
+ dest.writeInt(0);
+ int startPosition = dest.dataPosition();
+ // version 1
+ dest.writeInt(errorId);
+ dest.writeString(message);
+ // Go back and write the size
+ int parcelableSize = dest.dataPosition() - startPosition;
+ dest.setDataPosition(sizePosition);
+ dest.writeInt(parcelableSize);
+ dest.setDataPosition(startPosition + parcelableSize);
+ }
+
+ public static final Creator<OpenPgpError> CREATOR = new Creator<OpenPgpError>() {
+ public OpenPgpError createFromParcel(final Parcel source) {
+ int parcelableVersion = source.readInt();
+ int parcelableSize = source.readInt();
+ int startPosition = source.dataPosition();
+
+ OpenPgpError error = new OpenPgpError();
+ error.errorId = source.readInt();
+ error.message = source.readString();
+
+ // skip over all fields added in future versions of this parcel
+ source.setDataPosition(startPosition + parcelableSize);
+
+ return error;
+ }
+
+ public OpenPgpError[] newArray(final int size) {
+ return new OpenPgpError[size];
+ }
+ };
+}
diff --git a/openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpMetadata.java b/openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpMetadata.java
new file mode 100644
index 0000000..d620a57
--- /dev/null
+++ b/openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpMetadata.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parcelable versioning has been copied from Dashclock Widget
+ * https://code.google.com/p/dashclock/source/browse/api/src/main/java/com/google/android/apps/dashclock/api/ExtensionData.java
+ */
+public class OpenPgpMetadata implements Parcelable {
+ /**
+ * Since there might be a case where new versions of the client using the library getting
+ * old versions of the protocol (and thus old versions of this class), we need a versioning
+ * system for the parcels sent between the clients and the providers.
+ */
+ public static final int PARCELABLE_VERSION = 1;
+
+ String filename;
+ String mimeType;
+ long modificationTime;
+ long originalSize;
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public long getModificationTime() {
+ return modificationTime;
+ }
+
+ public long getOriginalSize() {
+ return originalSize;
+ }
+
+ public OpenPgpMetadata() {
+ }
+
+ public OpenPgpMetadata(String filename, String mimeType, long modificationTime,
+ long originalSize) {
+ this.filename = filename;
+ this.mimeType = mimeType;
+ this.modificationTime = modificationTime;
+ this.originalSize = originalSize;
+ }
+
+ public OpenPgpMetadata(OpenPgpMetadata b) {
+ this.filename = b.filename;
+ this.mimeType = b.mimeType;
+ this.modificationTime = b.modificationTime;
+ this.originalSize = b.originalSize;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ /**
+ * NOTE: When adding fields in the process of updating this API, make sure to bump
+ * {@link #PARCELABLE_VERSION}.
+ */
+ dest.writeInt(PARCELABLE_VERSION);
+ // Inject a placeholder that will store the parcel size from this point on
+ // (not including the size itself).
+ int sizePosition = dest.dataPosition();
+ dest.writeInt(0);
+ int startPosition = dest.dataPosition();
+ // version 1
+ dest.writeString(filename);
+ dest.writeString(mimeType);
+ dest.writeLong(modificationTime);
+ dest.writeLong(originalSize);
+ // Go back and write the size
+ int parcelableSize = dest.dataPosition() - startPosition;
+ dest.setDataPosition(sizePosition);
+ dest.writeInt(parcelableSize);
+ dest.setDataPosition(startPosition + parcelableSize);
+ }
+
+ public static final Creator<OpenPgpMetadata> CREATOR = new Creator<OpenPgpMetadata>() {
+ public OpenPgpMetadata createFromParcel(final Parcel source) {
+ int parcelableVersion = source.readInt();
+ int parcelableSize = source.readInt();
+ int startPosition = source.dataPosition();
+
+ OpenPgpMetadata vr = new OpenPgpMetadata();
+ vr.filename = source.readString();
+ vr.mimeType = source.readString();
+ vr.modificationTime = source.readLong();
+ vr.originalSize = source.readLong();
+
+ // skip over all fields added in future versions of this parcel
+ source.setDataPosition(startPosition + parcelableSize);
+
+ return vr;
+ }
+
+ public OpenPgpMetadata[] newArray(final int size) {
+ return new OpenPgpMetadata[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ String out = "\nfilename: " + filename;
+ out += "\nmimeType: " + mimeType;
+ out += "\nmodificationTime: " + modificationTime;
+ out += "\noriginalSize: " + originalSize;
+ return out;
+ }
+
+}
diff --git a/openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java b/openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java
new file mode 100644
index 0000000..6b5045d
--- /dev/null
+++ b/openpgp-api/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.openintents.openpgp.util.OpenPgpUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Parcelable versioning has been copied from Dashclock Widget
+ * https://code.google.com/p/dashclock/source/browse/api/src/main/java/com/google/android/apps/dashclock/api/ExtensionData.java
+ */
+public class OpenPgpSignatureResult implements Parcelable {
+ /**
+ * Since there might be a case where new versions of the client using the library getting
+ * old versions of the protocol (and thus old versions of this class), we need a versioning
+ * system for the parcels sent between the clients and the providers.
+ */
+ public static final int PARCELABLE_VERSION = 2;
+
+ // generic error on signature verification
+ public static final int SIGNATURE_ERROR = 0;
+ // successfully verified signature, with certified key
+ public static final int SIGNATURE_SUCCESS_CERTIFIED = 1;
+ // no key was found for this signature verification
+ public static final int SIGNATURE_KEY_MISSING = 2;
+ // successfully verified signature, but with uncertified key
+ public static final int SIGNATURE_SUCCESS_UNCERTIFIED = 3;
+ // key has been revoked
+ public static final int SIGNATURE_KEY_REVOKED = 4;
+ // key is expired
+ public static final int SIGNATURE_KEY_EXPIRED = 5;
+
+ int status;
+ boolean signatureOnly;
+ String primaryUserId;
+ ArrayList<String> userIds;
+ long keyId;
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+ public boolean isSignatureOnly() {
+ return signatureOnly;
+ }
+
+ public void setSignatureOnly(boolean signatureOnly) {
+ this.signatureOnly = signatureOnly;
+ }
+
+ public String getPrimaryUserId() {
+ return primaryUserId;
+ }
+
+ public void setPrimaryUserId(String primaryUserId) {
+ this.primaryUserId = primaryUserId;
+ }
+
+ public ArrayList<String> getUserIds() {
+ return userIds;
+ }
+
+ public void setUserIds(ArrayList<String> userIds) {
+ this.userIds = userIds;
+ }
+
+ public long getKeyId() {
+ return keyId;
+ }
+
+ public void setKeyId(long keyId) {
+ this.keyId = keyId;
+ }
+
+ public OpenPgpSignatureResult() {
+
+ }
+
+ public OpenPgpSignatureResult(int signatureStatus, String signatureUserId,
+ boolean signatureOnly, long keyId, ArrayList<String> userIds) {
+ this.status = signatureStatus;
+ this.signatureOnly = signatureOnly;
+ this.primaryUserId = signatureUserId;
+ this.keyId = keyId;
+ this.userIds = userIds;
+ }
+
+ public OpenPgpSignatureResult(OpenPgpSignatureResult b) {
+ this.status = b.status;
+ this.primaryUserId = b.primaryUserId;
+ this.signatureOnly = b.signatureOnly;
+ this.keyId = b.keyId;
+ this.userIds = b.userIds;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ /**
+ * NOTE: When adding fields in the process of updating this API, make sure to bump
+ * {@link #PARCELABLE_VERSION}.
+ */
+ dest.writeInt(PARCELABLE_VERSION);
+ // Inject a placeholder that will store the parcel size from this point on
+ // (not including the size itself).
+ int sizePosition = dest.dataPosition();
+ dest.writeInt(0);
+ int startPosition = dest.dataPosition();
+ // version 1
+ dest.writeInt(status);
+ dest.writeByte((byte) (signatureOnly ? 1 : 0));
+ dest.writeString(primaryUserId);
+ dest.writeLong(keyId);
+ // version 2
+ dest.writeStringList(userIds);
+ // Go back and write the size
+ int parcelableSize = dest.dataPosition() - startPosition;
+ dest.setDataPosition(sizePosition);
+ dest.writeInt(parcelableSize);
+ dest.setDataPosition(startPosition + parcelableSize);
+ }
+
+ public static final Creator<OpenPgpSignatureResult> CREATOR = new Creator<OpenPgpSignatureResult>() {
+ public OpenPgpSignatureResult createFromParcel(final Parcel source) {
+ int parcelableVersion = source.readInt();
+ int parcelableSize = source.readInt();
+ int startPosition = source.dataPosition();
+
+ OpenPgpSignatureResult vr = new OpenPgpSignatureResult();
+ vr.status = source.readInt();
+ vr.signatureOnly = source.readByte() == 1;
+ vr.primaryUserId = source.readString();
+ vr.keyId = source.readLong();
+ vr.userIds = new ArrayList<String>();
+ source.readStringList(vr.userIds);
+
+ // skip over all fields added in future versions of this parcel
+ source.setDataPosition(startPosition + parcelableSize);
+
+ return vr;
+ }
+
+ public OpenPgpSignatureResult[] newArray(final int size) {
+ return new OpenPgpSignatureResult[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ String out = "\nstatus: " + status;
+ out += "\nprimaryUserId: " + primaryUserId;
+ out += "\nuserIds: " + userIds;
+ out += "\nsignatureOnly: " + signatureOnly;
+ out += "\nkeyId: " + OpenPgpUtils.convertKeyIdToHex(keyId);
+ return out;
+ }
+
+}
diff --git a/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpApi.java b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpApi.java
new file mode 100644
index 0000000..cd1edc5
--- /dev/null
+++ b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpApi.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import org.openintents.openpgp.IOpenPgpService;
+import org.openintents.openpgp.OpenPgpError;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class OpenPgpApi {
+
+ public static final String TAG = "OpenPgp API";
+
+ public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
+
+ /**
+ * Version history
+ * ---------------
+ * <p/>
+ * 3:
+ * - First public stable version
+ * <p/>
+ * 4:
+ * - No changes to existing methods -> backward compatible
+ * - Introduction of ACTION_DECRYPT_METADATA, RESULT_METADATA, EXTRA_ORIGINAL_FILENAME, and OpenPgpMetadata parcel
+ * - Introduction of internal NFC extras: EXTRA_NFC_SIGNED_HASH, EXTRA_NFC_SIG_CREATION_TIMESTAMP
+ * 5:
+ * - OpenPgpSignatureResult: new consts SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED
+ * - OpenPgpSignatureResult: ArrayList<String> userIds
+ * 6:
+ * - Deprecate ACTION_SIGN
+ * - Introduce ACTION_CLEARTEXT_SIGN and ACTION_DETACHED_SIGN
+ * - New extra for ACTION_DETACHED_SIGN: EXTRA_DETACHED_SIGNATURE
+ * - New result for ACTION_DECRYPT_VERIFY: RESULT_DETACHED_SIGNATURE
+ * - New result for ACTION_DECRYPT_VERIFY: RESULT_CHARSET
+ * 7:
+ * - Deprecation of ACCOUNT_NAME, please use ACTION_GET_SIGN_KEY_ID to get key id
+ * - Introduce EXTRA_SIGN_KEY_ID
+ * - New extra for ACTION_ENCRYPT and ACTION_SIGN_AND_ENCRYPT: EXTRA_ENABLE_COMPRESSION (default to true)
+ * - Return PendingIntent to view key for signatures
+ * - New result for ACTION_DECRYPT_VERIFY: RESULT_TYPE
+ * - New ACTION_GET_SIGN_KEY_ID
+ * - EXTRA_PASSPHRASE changed from String to char[]
+ */
+ public static final int API_VERSION = 7;
+
+ /**
+ * General extras
+ * --------------
+ *
+ * required extras:
+ * int EXTRA_API_VERSION (always required)
+ *
+ * returned extras:
+ * int RESULT_CODE (RESULT_CODE_ERROR, RESULT_CODE_SUCCESS or RESULT_CODE_USER_INTERACTION_REQUIRED)
+ * OpenPgpError RESULT_ERROR (if RESULT_CODE == RESULT_CODE_ERROR)
+ * PendingIntent RESULT_INTENT (if RESULT_CODE == RESULT_CODE_USER_INTERACTION_REQUIRED)
+ */
+
+ /**
+ * DEPRECATED
+ * Same as ACTION_CLEARTEXT_SIGN
+ * <p/>
+ * optional extras:
+ * boolean EXTRA_REQUEST_ASCII_ARMOR (DEPRECATED: this makes no sense here)
+ * char[] EXTRA_PASSPHRASE (key passphrase)
+ */
+ public static final String ACTION_SIGN = "org.openintents.openpgp.action.SIGN";
+
+ /**
+ * Sign text resulting in a cleartext signature
+ * Some magic pre-processing of the text is done to convert it to a format usable for
+ * cleartext signatures per RFC 4880 before the text is actually signed:
+ * - end cleartext with newline
+ * - remove whitespaces on line endings
+ * <p/>
+ * required extras:
+ * long EXTRA_SIGN_KEY_ID (key id of signing key)
+ * <p/>
+ * optional extras:
+ * char[] EXTRA_PASSPHRASE (key passphrase)
+ */
+ public static final String ACTION_CLEARTEXT_SIGN = "org.openintents.openpgp.action.CLEARTEXT_SIGN";
+
+ /**
+ * Sign text or binary data resulting in a detached signature.
+ * No OutputStream necessary for ACTION_DETACHED_SIGN (No magic pre-processing like in ACTION_CLEARTEXT_SIGN)!
+ * The detached signature is returned separately in RESULT_DETACHED_SIGNATURE.
+ * <p/>
+ * required extras:
+ * long EXTRA_SIGN_KEY_ID (key id of signing key)
+ * <p/>
+ * optional extras:
+ * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for detached signature)
+ * char[] EXTRA_PASSPHRASE (key passphrase)
+ * <p/>
+ * returned extras:
+ * byte[] RESULT_DETACHED_SIGNATURE
+ */
+ public static final String ACTION_DETACHED_SIGN = "org.openintents.openpgp.action.DETACHED_SIGN";
+
+ /**
+ * Encrypt
+ * <p/>
+ * required extras:
+ * String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT)
+ * or
+ * long[] EXTRA_KEY_IDS
+ * <p/>
+ * optional extras:
+ * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for output)
+ * char[] EXTRA_PASSPHRASE (key passphrase)
+ * String EXTRA_ORIGINAL_FILENAME (original filename to be encrypted as metadata)
+ * boolean EXTRA_ENABLE_COMPRESSION (enable ZLIB compression, default ist true)
+ */
+ public static final String ACTION_ENCRYPT = "org.openintents.openpgp.action.ENCRYPT";
+
+ /**
+ * Sign and encrypt
+ * <p/>
+ * required extras:
+ * String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT)
+ * or
+ * long[] EXTRA_KEY_IDS
+ * <p/>
+ * optional extras:
+ * long EXTRA_SIGN_KEY_ID (key id of signing key)
+ * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for output)
+ * char[] EXTRA_PASSPHRASE (key passphrase)
+ * String EXTRA_ORIGINAL_FILENAME (original filename to be encrypted as metadata)
+ * boolean EXTRA_ENABLE_COMPRESSION (enable ZLIB compression, default ist true)
+ */
+ public static final String ACTION_SIGN_AND_ENCRYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT";
+
+ /**
+ * Decrypts and verifies given input stream. This methods handles encrypted-only, signed-and-encrypted,
+ * and also signed-only input.
+ * OutputStream is optional, e.g., for verifying detached signatures!
+ * <p/>
+ * If OpenPgpSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING
+ * in addition a PendingIntent is returned via RESULT_INTENT to download missing keys.
+ * On all other status, in addition a PendingIntent is returned via RESULT_INTENT to open
+ * the key view in OpenKeychain.
+ * <p/>
+ * optional extras:
+ * byte[] EXTRA_DETACHED_SIGNATURE (detached signature)
+ * <p/>
+ * returned extras:
+ * OpenPgpSignatureResult RESULT_SIGNATURE
+ * OpenPgpDecryptMetadata RESULT_METADATA
+ * String RESULT_CHARSET (charset which was specified in the headers of ascii armored input, if any)
+ * int RESULT_TYPE
+ */
+ public static final String ACTION_DECRYPT_VERIFY = "org.openintents.openpgp.action.DECRYPT_VERIFY";
+
+ /**
+ * Decrypts the header of an encrypted file to retrieve metadata such as original filename.
+ * <p/>
+ * This does not decrypt the actual content of the file.
+ * <p/>
+ * returned extras:
+ * OpenPgpDecryptMetadata RESULT_METADATA
+ * String RESULT_CHARSET (charset which was specified in the headers of ascii armored input, if any)
+ */
+ public static final String ACTION_DECRYPT_METADATA = "org.openintents.openpgp.action.DECRYPT_METADATA";
+
+ /**
+ * Select key id for signing
+ * <p/>
+ * optional extras:
+ * String EXTRA_USER_ID
+ * <p/>
+ * returned extras:
+ * long EXTRA_SIGN_KEY_ID
+ */
+ public static final String ACTION_GET_SIGN_KEY_ID = "org.openintents.openpgp.action.GET_SIGN_KEY_ID";
+
+ /**
+ * Get key ids based on given user ids (=emails)
+ * <p/>
+ * required extras:
+ * String[] EXTRA_USER_IDS
+ * <p/>
+ * returned extras:
+ * long[] RESULT_KEY_IDS
+ */
+ public static final String ACTION_GET_KEY_IDS = "org.openintents.openpgp.action.GET_KEY_IDS";
+
+ /**
+ * This action returns RESULT_CODE_SUCCESS if the OpenPGP Provider already has the key
+ * corresponding to the given key id in its database.
+ * <p/>
+ * It returns RESULT_CODE_USER_INTERACTION_REQUIRED if the Provider does not have the key.
+ * The PendingIntent from RESULT_INTENT can be used to retrieve those from a keyserver.
+ * <p/>
+ * required extras:
+ * long EXTRA_KEY_ID
+ */
+ public static final String ACTION_GET_KEY = "org.openintents.openpgp.action.GET_KEY";
+
+
+ /* Intent extras */
+ public static final String EXTRA_API_VERSION = "api_version";
+
+ // DEPRECATED!!!
+ public static final String EXTRA_ACCOUNT_NAME = "account_name";
+
+ // ACTION_DETACHED_SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY
+ // request ASCII Armor for output
+ // OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
+ public static final String EXTRA_REQUEST_ASCII_ARMOR = "ascii_armor";
+
+ // ACTION_DETACHED_SIGN
+ public static final String RESULT_DETACHED_SIGNATURE = "detached_signature";
+
+ // ENCRYPT, SIGN_AND_ENCRYPT
+ public static final String EXTRA_USER_IDS = "user_ids";
+ public static final String EXTRA_KEY_IDS = "key_ids";
+ public static final String EXTRA_SIGN_KEY_ID = "sign_key_id";
+ // optional extras:
+ public static final String EXTRA_PASSPHRASE = "passphrase";
+ public static final String EXTRA_ORIGINAL_FILENAME = "original_filename";
+ public static final String EXTRA_ENABLE_COMPRESSION = "enable_compression";
+
+ // GET_SIGN_KEY_ID
+ public static final String EXTRA_USER_ID = "user_id";
+
+ // GET_KEY
+ public static final String EXTRA_KEY_ID = "key_id";
+ public static final String RESULT_KEY_IDS = "key_ids";
+
+ /* Service Intent returns */
+ public static final String RESULT_CODE = "result_code";
+
+ // get actual error object from RESULT_ERROR
+ public static final int RESULT_CODE_ERROR = 0;
+ // success!
+ public static final int RESULT_CODE_SUCCESS = 1;
+ // get PendingIntent from RESULT_INTENT, start PendingIntent with startIntentSenderForResult,
+ // and execute service method again in onActivityResult
+ public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2;
+
+ public static final String RESULT_ERROR = "error";
+ public static final String RESULT_INTENT = "intent";
+
+ // DECRYPT_VERIFY
+ public static final String EXTRA_DETACHED_SIGNATURE = "detached_signature";
+ public static final String RESULT_SIGNATURE = "signature";
+ public static final String RESULT_METADATA = "metadata";
+ // This will be the charset which was specified in the headers of ascii armored input, if any
+ public static final String RESULT_CHARSET = "charset";
+
+ public static final String RESULT_TYPE = "type";
+ public static final int RESULT_TYPE_UNENCRYPTED_UNSIGNED = 0;
+ public static final int RESULT_TYPE_ENCRYPTED = 1;
+ public static final int RESULT_TYPE_SIGNED = 2;
+
+ // INTERNAL, should not be used
+ public static final String EXTRA_CALL_UUID1 = "call_uuid1";
+ public static final String EXTRA_CALL_UUID2 = "call_uuid2";
+
+ IOpenPgpService mService;
+ Context mContext;
+
+ public OpenPgpApi(Context context, IOpenPgpService service) {
+ this.mContext = context;
+ this.mService = service;
+ }
+
+ public interface IOpenPgpCallback {
+ void onReturn(final Intent result);
+ }
+
+ private class OpenPgpAsyncTask extends AsyncTask<Void, Integer, Intent> {
+ Intent data;
+ InputStream is;
+ OutputStream os;
+ IOpenPgpCallback callback;
+
+ private OpenPgpAsyncTask(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) {
+ this.data = data;
+ this.is = is;
+ this.os = os;
+ this.callback = callback;
+ }
+
+ @Override
+ protected Intent doInBackground(Void... unused) {
+ return executeApi(data, is, os);
+ }
+
+ protected void onPostExecute(Intent result) {
+ callback.onReturn(result);
+ }
+
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void executeApiAsync(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) {
+ OpenPgpAsyncTask task = new OpenPgpAsyncTask(data, is, os, callback);
+
+ // don't serialize async tasks!
+ // http://commonsware.com/blog/2012/04/20/asynctask-threading-regression-confirmed.html
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ } else {
+ task.execute((Void[]) null);
+ }
+ }
+
+ /**
+ * InputStream and OutputStreams are always closed after operating on them!
+ *
+ * @param data
+ * @param is
+ * @param os
+ * @return
+ */
+ public Intent executeApi(Intent data, InputStream is, OutputStream os) {
+ ParcelFileDescriptor input = null;
+ ParcelFileDescriptor output = null;
+ try {
+ // always send version from client
+ data.putExtra(EXTRA_API_VERSION, OpenPgpApi.API_VERSION);
+
+ Intent result;
+
+ // pipe the input and output
+ if (is != null) {
+ input = ParcelFileDescriptorUtil.pipeFrom(is,
+ new ParcelFileDescriptorUtil.IThreadListener() {
+
+ @Override
+ public void onThreadFinished(Thread thread) {
+ //Log.d(OpenPgpApi.TAG, "Copy to service finished");
+ }
+ }
+ );
+ }
+ if (os != null) {
+ output = ParcelFileDescriptorUtil.pipeTo(os,
+ new ParcelFileDescriptorUtil.IThreadListener() {
+
+ @Override
+ public void onThreadFinished(Thread thread) {
+ //Log.d(OpenPgpApi.TAG, "Service finished writing!");
+ }
+ }
+ );
+ }
+
+ // blocks until result is ready
+ result = mService.execute(data, input, output);
+
+ // set class loader to current context to allow unparcelling
+ // of OpenPgpError and OpenPgpSignatureResult
+ // http://stackoverflow.com/a/3806769
+ result.setExtrasClassLoader(mContext.getClassLoader());
+
+ return result;
+ } catch (Exception e) {
+ Log.e(OpenPgpApi.TAG, "Exception in executeApi call", e);
+ Intent result = new Intent();
+ result.putExtra(RESULT_CODE, RESULT_CODE_ERROR);
+ result.putExtra(RESULT_ERROR,
+ new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage()));
+ return result;
+ } finally {
+ // close() is required to halt the TransferThread
+ if (output != null) {
+ try {
+ output.close();
+ } catch (IOException e) {
+ Log.e(OpenPgpApi.TAG, "IOException when closing ParcelFileDescriptor!", e);
+ }
+ }
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ Log.e(OpenPgpApi.TAG, "IOException when closing ParcelFileDescriptor!", e);
+ }
+ }
+ }
+ }
+
+}
diff --git a/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpAppPreference.java b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpAppPreference.java
new file mode 100644
index 0000000..4bf4cec
--- /dev/null
+++ b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpAppPreference.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.preference.DialogPreference;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import org.openintents.openpgp.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Does not extend ListPreference, but is very similar to it!
+ * http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/preference/ListPreference.java/?v=source
+ */
+public class OpenPgpAppPreference extends DialogPreference {
+ private static final String OPENKEYCHAIN_PACKAGE = "org.sufficientlysecure.keychain";
+ private static final String MARKET_INTENT_URI_BASE = "market://details?id=%s";
+ private static final Intent MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse(
+ String.format(MARKET_INTENT_URI_BASE, OPENKEYCHAIN_PACKAGE)));
+
+ private static final ArrayList<String> PROVIDER_BLACKLIST = new ArrayList<String>();
+
+ static {
+ // Unfortunately, the current released version of APG includes a broken version of the API
+ PROVIDER_BLACKLIST.add("org.thialfihar.android.apg");
+ }
+
+ private ArrayList<OpenPgpProviderEntry> mLegacyList = new ArrayList<>();
+ private ArrayList<OpenPgpProviderEntry> mList = new ArrayList<>();
+
+ private String mSelectedPackage;
+
+ public OpenPgpAppPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ populateAppList();
+ }
+
+ public OpenPgpAppPreference(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Public method to add new entries for legacy applications
+ *
+ * @param packageName
+ * @param simpleName
+ * @param icon
+ */
+ public void addLegacyProvider(int position, String packageName, String simpleName, Drawable icon) {
+ mLegacyList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon));
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(Builder builder) {
+
+ // do again, maybe an app has now been installed
+ populateAppList();
+
+ // Init ArrayAdapter with OpenPGP Providers
+ ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
+ android.R.layout.select_dialog_singlechoice, android.R.id.text1, mList) {
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // User super class to create the View
+ View v = super.getView(position, convertView, parent);
+ TextView tv = (TextView) v.findViewById(android.R.id.text1);
+
+ // Put the image on the TextView
+ tv.setCompoundDrawablesWithIntrinsicBounds(mList.get(position).icon, null,
+ null, null);
+
+ // Add margin between image and text (support various screen densities)
+ int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
+ tv.setCompoundDrawablePadding(dp10);
+
+ return v;
+ }
+ };
+
+ builder.setSingleChoiceItems(adapter, getIndexOfProviderList(mSelectedPackage),
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ OpenPgpProviderEntry entry = mList.get(which);
+
+ if (entry.intent != null) {
+ /*
+ * Intents are called as activity
+ *
+ * Current approach is to assume the user installed the app.
+ * If he does not, the selected package is not valid.
+ *
+ * However applications should always consider this could happen,
+ * as the user might remove the currently used OpenPGP app.
+ */
+ getContext().startActivity(entry.intent);
+ return;
+ }
+
+ mSelectedPackage = entry.packageName;
+
+ /*
+ * Clicking on an item simulates the positive button click, and dismisses
+ * the dialog.
+ */
+ OpenPgpAppPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
+ dialog.dismiss();
+ }
+ });
+
+ /*
+ * The typical interaction for list-based dialogs is to have click-on-an-item dismiss the
+ * dialog instead of the user having to press 'Ok'.
+ */
+ builder.setPositiveButton(null, null);
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+
+ if (positiveResult && (mSelectedPackage != null)) {
+ save();
+ }
+ }
+
+ private void save() {
+ // Give the client a chance to ignore this change if they deem it
+ // invalid
+ if (!callChangeListener(mSelectedPackage)) {
+ // They don't want the value to be set
+ return;
+ }
+
+ setAndPersist(mSelectedPackage);
+ }
+
+ private void setAndPersist(String packageName) {
+ mSelectedPackage = packageName;
+
+ // Save to persistent storage (this method will make sure this
+ // preference should be persistent, along with other useful checks)
+ persistString(mSelectedPackage);
+
+ // Data has changed, notify so UI can be refreshed!
+ notifyChanged();
+
+ // also update summary with selected provider
+ updateSummary(mSelectedPackage);
+ }
+
+ private void updateSummary(String packageName) {
+ String summary = getEntryByValue(packageName);
+ setSummary(summary);
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return getEntryByValue(mSelectedPackage);
+ }
+
+ private int getIndexOfProviderList(String packageName) {
+ for (OpenPgpProviderEntry app : mList) {
+ if (app.packageName.equals(packageName)) {
+ return mList.indexOf(app);
+ }
+ }
+
+ // default is "none"
+ return 0;
+ }
+
+ /**
+ * Public API
+ */
+ public String getEntry() {
+ return getEntryByValue(mSelectedPackage);
+ }
+
+ /**
+ * Public API
+ */
+ public String getValue() {
+ return mSelectedPackage;
+ }
+
+ /**
+ * Public API
+ */
+ public void setValue(String packageName) {
+ setAndPersist(packageName);
+ }
+
+ @Override
+ protected Object onGetDefaultValue(TypedArray a, int index) {
+ return a.getString(index);
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+ if (restoreValue) {
+ // Restore state
+ mSelectedPackage = getPersistedString(mSelectedPackage);
+ updateSummary(mSelectedPackage);
+ } else {
+ String value = (String) defaultValue;
+ setAndPersist(value);
+ updateSummary(value);
+ }
+ }
+
+ public String getEntryByValue(String packageName) {
+ for (OpenPgpProviderEntry app : mList) {
+ if (app.packageName.equals(packageName) && app.intent == null) {
+ return app.simpleName;
+ }
+ }
+
+ return getContext().getString(R.string.openpgp_list_preference_none);
+ }
+
+ private void populateAppList() {
+ mList.clear();
+
+ // add "none"-entry
+ mList.add(0, new OpenPgpProviderEntry("",
+ getContext().getString(R.string.openpgp_list_preference_none),
+ getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize)));
+
+ // add all additional (legacy) providers
+ mList.addAll(mLegacyList);
+
+ // search for OpenPGP providers...
+ ArrayList<OpenPgpProviderEntry> providerList = new ArrayList<>();
+ Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT);
+ List<ResolveInfo> resInfo = getContext().getPackageManager().queryIntentServices(intent, 0);
+ if (!resInfo.isEmpty()) {
+ for (ResolveInfo resolveInfo : resInfo) {
+ if (resolveInfo.serviceInfo == null)
+ continue;
+
+ String packageName = resolveInfo.serviceInfo.packageName;
+ String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(getContext()
+ .getPackageManager()));
+ Drawable icon = resolveInfo.serviceInfo.loadIcon(getContext().getPackageManager());
+
+ if (!PROVIDER_BLACKLIST.contains(packageName)) {
+ providerList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
+ }
+ }
+ }
+
+ if (providerList.isEmpty()) {
+ // add install links if provider list is empty
+ resInfo = getContext().getPackageManager().queryIntentActivities
+ (MARKET_INTENT, 0);
+ for (ResolveInfo resolveInfo : resInfo) {
+ Intent marketIntent = new Intent(MARKET_INTENT);
+ marketIntent.setPackage(resolveInfo.activityInfo.packageName);
+ Drawable icon = resolveInfo.activityInfo.loadIcon(getContext().getPackageManager());
+ String marketName = String.valueOf(resolveInfo.activityInfo.applicationInfo
+ .loadLabel(getContext().getPackageManager()));
+ String simpleName = String.format(getContext().getString(R.string
+ .openpgp_install_openkeychain_via), marketName);
+ mList.add(new OpenPgpProviderEntry(OPENKEYCHAIN_PACKAGE, simpleName,
+ icon, marketIntent));
+ }
+ } else {
+ // add provider
+ mList.addAll(providerList);
+ }
+ }
+
+ private static class OpenPgpProviderEntry {
+ private String packageName;
+ private String simpleName;
+ private Drawable icon;
+ private Intent intent;
+
+ public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon) {
+ this.packageName = packageName;
+ this.simpleName = simpleName;
+ this.icon = icon;
+ }
+
+ public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon, Intent intent) {
+ this(packageName, simpleName, icon);
+ this.intent = intent;
+ }
+
+ @Override
+ public String toString() {
+ return simpleName;
+ }
+ }
+}
diff --git a/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpKeyPreference.java b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpKeyPreference.java
new file mode 100644
index 0000000..6343285
--- /dev/null
+++ b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpKeyPreference.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.preference.Preference;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import org.openintents.openpgp.IOpenPgpService;
+import org.openintents.openpgp.OpenPgpError;
+import org.openintents.openpgp.R;
+
+public class OpenPgpKeyPreference extends Preference {
+ private long mKeyId;
+ private String mOpenPgpProvider;
+ private OpenPgpServiceConnection mServiceConnection;
+ private String mDefaultUserId;
+
+ public static final int REQUEST_CODE_KEY_PREFERENCE = 9999;
+
+ public OpenPgpKeyPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return (mKeyId == 0) ? getContext().getString(R.string.openpgp_no_key_selected)
+ : getContext().getString(R.string.openpgp_key_selected);
+ }
+
+ private void updateEnabled() {
+ if (TextUtils.isEmpty(mOpenPgpProvider)) {
+ setEnabled(false);
+ } else {
+ setEnabled(true);
+ }
+ }
+
+ public void setOpenPgpProvider(String packageName) {
+ mOpenPgpProvider = packageName;
+ updateEnabled();
+ }
+
+ public void setDefaultUserId(String userId) {
+ mDefaultUserId = userId;
+ }
+
+ @Override
+ protected void onClick() {
+ // bind to service
+ mServiceConnection = new OpenPgpServiceConnection(
+ getContext().getApplicationContext(),
+ mOpenPgpProvider,
+ new OpenPgpServiceConnection.OnBound() {
+ @Override
+ public void onBound(IOpenPgpService service) {
+ Log.d(OpenPgpApi.TAG, "onBound!");
+
+ Intent data = new Intent();
+ data.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
+ data.putExtra(OpenPgpApi.EXTRA_USER_ID, mDefaultUserId);
+
+ OpenPgpApi api = new OpenPgpApi(getContext(), mServiceConnection.getService());
+ api.executeApiAsync(data, null, null, new MyCallback(REQUEST_CODE_KEY_PREFERENCE));
+ }
+
+ @Override
+ public void onError(Exception e) {
+ Log.e(OpenPgpApi.TAG, "exception when binding!", e);
+ }
+ }
+ );
+ mServiceConnection.bindToService();
+ }
+
+ private class MyCallback implements OpenPgpApi.IOpenPgpCallback {
+ int requestCode;
+
+ private MyCallback(int requestCode) {
+ this.requestCode = requestCode;
+ }
+
+ @Override
+ public void onReturn(Intent result) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
+ case OpenPgpApi.RESULT_CODE_SUCCESS: {
+ Log.e(OpenPgpApi.TAG, "RESULT_CODE_SUCCESS: Should not happen!");
+
+ break;
+ }
+ case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
+
+ PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
+ try {
+ Activity act = (Activity) getContext();
+ act.startIntentSenderFromChild(
+ act, pi.getIntentSender(),
+ requestCode, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Log.e(OpenPgpApi.TAG, "SendIntentException", e);
+ }
+ break;
+ }
+ case OpenPgpApi.RESULT_CODE_ERROR: {
+ OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
+ Log.e(OpenPgpApi.TAG, "RESULT_CODE_ERROR: " + error.getMessage());
+
+ break;
+ }
+ }
+ }
+ }
+
+ private void save(long newValue) {
+ // Give the client a chance to ignore this change if they deem it
+ // invalid
+ if (!callChangeListener(newValue)) {
+ // They don't want the value to be set
+ return;
+ }
+
+ setAndPersist(newValue);
+ }
+
+ /**
+ * Public API
+ */
+ public void setValue(long keyId) {
+ setAndPersist(keyId);
+ }
+
+ /**
+ * Public API
+ */
+ public long getValue() {
+ return mKeyId;
+ }
+
+ private void setAndPersist(long newValue) {
+ mKeyId = newValue;
+
+ // Save to persistent storage (this method will make sure this
+ // preference should be persistent, along with other useful checks)
+ persistLong(mKeyId);
+
+ // Data has changed, notify so UI can be refreshed!
+ notifyChanged();
+ }
+
+ @Override
+ protected Object onGetDefaultValue(TypedArray a, int index) {
+ // This preference type's value type is Long, so we read the default
+ // value from the attributes as an Integer.
+ return (long) a.getInteger(index, 0);
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+ if (restoreValue) {
+ // Restore state
+ mKeyId = getPersistedLong(mKeyId);
+ } else {
+ // Set state
+ long value = (Long) defaultValue;
+ setAndPersist(value);
+ }
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ /*
+ * Suppose a client uses this preference type without persisting. We
+ * must save the instance state so it is able to, for example, survive
+ * orientation changes.
+ */
+
+ final Parcelable superState = super.onSaveInstanceState();
+ if (isPersistent()) {
+ // No need to save instance state since it's persistent
+ return superState;
+ }
+
+ // Save the instance state
+ final SavedState myState = new SavedState(superState);
+ myState.keyId = mKeyId;
+ myState.openPgpProvider = mOpenPgpProvider;
+ myState.defaultUserId = mDefaultUserId;
+ return myState;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (!state.getClass().equals(SavedState.class)) {
+ // Didn't save state for us in onSaveInstanceState
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ // Restore the instance state
+ SavedState myState = (SavedState) state;
+ super.onRestoreInstanceState(myState.getSuperState());
+ mKeyId = myState.keyId;
+ mOpenPgpProvider = myState.openPgpProvider;
+ mDefaultUserId = myState.defaultUserId;
+ notifyChanged();
+ }
+
+ /**
+ * SavedState, a subclass of {@link BaseSavedState}, will store the state
+ * of MyPreference, a subclass of Preference.
+ * <p/>
+ * It is important to always call through to super methods.
+ */
+ private static class SavedState extends BaseSavedState {
+ long keyId;
+ String openPgpProvider;
+ String defaultUserId;
+
+ public SavedState(Parcel source) {
+ super(source);
+
+ keyId = source.readInt();
+ openPgpProvider = source.readString();
+ defaultUserId = source.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+
+ dest.writeLong(keyId);
+ dest.writeString(openPgpProvider);
+ dest.writeString(defaultUserId);
+ }
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_KEY_PREFERENCE && resultCode == Activity.RESULT_OK) {
+ long keyId = data.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, 0);
+ save(keyId);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpServiceConnection.java b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpServiceConnection.java
new file mode 100644
index 0000000..bbc8645
--- /dev/null
+++ b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpServiceConnection.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+import org.openintents.openpgp.IOpenPgpService;
+
+public class OpenPgpServiceConnection {
+
+ // callback interface
+ public interface OnBound {
+ public void onBound(IOpenPgpService service);
+
+ public void onError(Exception e);
+ }
+
+ private Context mApplicationContext;
+
+ private IOpenPgpService mService;
+ private String mProviderPackageName;
+
+ private OnBound mOnBoundListener;
+
+ /**
+ * Create new connection
+ *
+ * @param context
+ * @param providerPackageName specify package name of OpenPGP provider,
+ * e.g., "org.sufficientlysecure.keychain"
+ */
+ public OpenPgpServiceConnection(Context context, String providerPackageName) {
+ this.mApplicationContext = context.getApplicationContext();
+ this.mProviderPackageName = providerPackageName;
+ }
+
+ /**
+ * Create new connection with callback
+ *
+ * @param context
+ * @param providerPackageName specify package name of OpenPGP provider,
+ * e.g., "org.sufficientlysecure.keychain"
+ * @param onBoundListener callback, executed when connection to service has been established
+ */
+ public OpenPgpServiceConnection(Context context, String providerPackageName,
+ OnBound onBoundListener) {
+ this(context, providerPackageName);
+ this.mOnBoundListener = onBoundListener;
+ }
+
+ public IOpenPgpService getService() {
+ return mService;
+ }
+
+ public boolean isBound() {
+ return (mService != null);
+ }
+
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = IOpenPgpService.Stub.asInterface(service);
+ if (mOnBoundListener != null) {
+ mOnBoundListener.onBound(mService);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ /**
+ * If not already bound, bind to service!
+ *
+ * @return
+ */
+ public void bindToService() {
+ // if not already bound...
+ if (mService == null) {
+ try {
+ Intent serviceIntent = new Intent(OpenPgpApi.SERVICE_INTENT);
+ // NOTE: setPackage is very important to restrict the intent to this provider only!
+ serviceIntent.setPackage(mProviderPackageName);
+ boolean connect = mApplicationContext.bindService(serviceIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ if (!connect) {
+ throw new Exception("bindService() returned false!");
+ }
+ } catch (Exception e) {
+ if (mOnBoundListener != null) {
+ mOnBoundListener.onError(e);
+ }
+ }
+ } else {
+ // already bound, but also inform client about it with callback
+ if (mOnBoundListener != null) {
+ mOnBoundListener.onBound(mService);
+ }
+ }
+ }
+
+ public void unbindFromService() {
+ mApplicationContext.unbindService(mServiceConnection);
+ }
+
+}
diff --git a/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpUtils.java b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpUtils.java
new file mode 100644
index 0000000..ad5f47b
--- /dev/null
+++ b/openpgp-api/src/main/java/org/openintents/openpgp/util/OpenPgpUtils.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.text.TextUtils;
+
+public class OpenPgpUtils {
+
+ public static final Pattern PGP_MESSAGE = Pattern.compile(
+ ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
+ Pattern.DOTALL);
+
+ public static final Pattern PGP_SIGNED_MESSAGE = Pattern.compile(
+ ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
+ Pattern.DOTALL);
+
+ public static final int PARSE_RESULT_NO_PGP = -1;
+ public static final int PARSE_RESULT_MESSAGE = 0;
+ public static final int PARSE_RESULT_SIGNED_MESSAGE = 1;
+
+ public static int parseMessage(String message) {
+ Matcher matcherSigned = PGP_SIGNED_MESSAGE.matcher(message);
+ Matcher matcherMessage = PGP_MESSAGE.matcher(message);
+
+ if (matcherMessage.matches()) {
+ return PARSE_RESULT_MESSAGE;
+ } else if (matcherSigned.matches()) {
+ return PARSE_RESULT_SIGNED_MESSAGE;
+ } else {
+ return PARSE_RESULT_NO_PGP;
+ }
+ }
+
+ public static boolean isAvailable(Context context) {
+ Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT);
+ List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
+ return !resInfo.isEmpty();
+ }
+
+ public static String convertKeyIdToHex(long keyId) {
+ return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
+ }
+
+ private static String convertKeyIdToHex32bit(long keyId) {
+ String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.ENGLISH);
+ while (hexString.length() < 8) {
+ hexString = "0" + hexString;
+ }
+ return hexString;
+ }
+
+
+ private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
+
+ /**
+ * Splits userId string into naming part, email part, and comment part
+ * <p/>
+ * User ID matching:
+ * http://fiddle.re/t4p6f
+ *
+ * @param userId
+ * @return theParsedUserInfo
+ */
+ public static UserId splitUserId(final String userId) {
+ if (!TextUtils.isEmpty(userId)) {
+ final Matcher matcher = USER_ID_PATTERN.matcher(userId);
+ if (matcher.matches()) {
+ return new UserId(matcher.group(1), matcher.group(3), matcher.group(2));
+ }
+ }
+ return new UserId(null, null, null);
+ }
+
+ /**
+ * Returns a composed user id. Returns null if name is null!
+ *
+ * @param name
+ * @param email
+ * @param comment
+ * @return
+ */
+ public static String createUserId(UserId userId) {
+ String userIdString = userId.name; // consider name a required value
+ if (userIdString != null && !TextUtils.isEmpty(userId.comment)) {
+ userIdString += " (" + userId.comment + ")";
+ }
+ if (userIdString != null && !TextUtils.isEmpty(userId.email)) {
+ userIdString += " <" + userId.email + ">";
+ }
+
+ return userIdString;
+ }
+
+ public static class UserId {
+ public final String name;
+ public final String email;
+ public final String comment;
+
+ public UserId(String name, String email, String comment) {
+ this.name = name;
+ this.email = email;
+ this.comment = comment;
+ }
+ }
+}
diff --git a/openpgp-api/src/main/java/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java b/openpgp-api/src/main/java/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java
new file mode 100644
index 0000000..b9492f9
--- /dev/null
+++ b/openpgp-api/src/main/java/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * 2013 Florian Schmaus <flo@geekplace.eu>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import android.os.ParcelFileDescriptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Partially based on <a href="http://stackoverflow.com/questions/18212152/">Stackoverflow: Transfer InputStream to another Service (across process boundaries)</a>
+ **/
+public class ParcelFileDescriptorUtil {
+
+ public interface IThreadListener {
+ void onThreadFinished(final Thread thread);
+ }
+
+ public static ParcelFileDescriptor pipeFrom(InputStream inputStream, IThreadListener listener)
+ throws IOException {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ ParcelFileDescriptor readSide = pipe[0];
+ ParcelFileDescriptor writeSide = pipe[1];
+
+ // start the transfer thread
+ new TransferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide),
+ listener)
+ .start();
+
+ return readSide;
+ }
+
+ public static ParcelFileDescriptor pipeTo(OutputStream outputStream, IThreadListener listener)
+ throws IOException {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ ParcelFileDescriptor readSide = pipe[0];
+ ParcelFileDescriptor writeSide = pipe[1];
+
+ // start the transfer thread
+ new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream,
+ listener)
+ .start();
+
+ return writeSide;
+ }
+
+ static class TransferThread extends Thread {
+ final InputStream mIn;
+ final OutputStream mOut;
+ final IThreadListener mListener;
+
+ TransferThread(InputStream in, OutputStream out, IThreadListener listener) {
+ super("ParcelFileDescriptor Transfer Thread");
+ mIn = in;
+ mOut = out;
+ mListener = listener;
+ setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ byte[] buf = new byte[1024];
+ int len;
+
+ try {
+ while ((len = mIn.read(buf)) > 0) {
+ mOut.write(buf, 0, len);
+ }
+ mOut.flush(); // just to be safe
+ } catch (IOException e) {
+ //Log.e(OpenPgpApi.TAG, "TransferThread" + getId() + ": writing failed", e);
+ } finally {
+ try {
+ mIn.close();
+ } catch (IOException e) {
+ //Log.e(OpenPgpApi.TAG, "TransferThread" + getId(), e);
+ }
+ try {
+ mOut.close();
+ } catch (IOException e) {
+ //Log.e(OpenPgpApi.TAG, "TransferThread" + getId(), e);
+ }
+ }
+ if (mListener != null) {
+ //Log.d(OpenPgpApi.TAG, "TransferThread " + getId() + " finished!");
+ mListener.onThreadFinished(this);
+ }
+ }
+ }
+}
diff --git a/openpgp-api/src/main/res/drawable-hdpi/ic_action_cancel_launchersize.png b/openpgp-api/src/main/res/drawable-hdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 0000000..71b9118
--- /dev/null
+++ b/openpgp-api/src/main/res/drawable-hdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/openpgp-api/src/main/res/drawable-hdpi/ic_action_cancel_launchersize_light.png b/openpgp-api/src/main/res/drawable-hdpi/ic_action_cancel_launchersize_light.png
new file mode 100644
index 0000000..73b1d08
--- /dev/null
+++ b/openpgp-api/src/main/res/drawable-hdpi/ic_action_cancel_launchersize_light.png
Binary files differ
diff --git a/openpgp-api/src/main/res/drawable-mdpi/ic_action_cancel_launchersize.png b/openpgp-api/src/main/res/drawable-mdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 0000000..270abf4
--- /dev/null
+++ b/openpgp-api/src/main/res/drawable-mdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/openpgp-api/src/main/res/drawable-mdpi/ic_action_cancel_launchersize_light.png b/openpgp-api/src/main/res/drawable-mdpi/ic_action_cancel_launchersize_light.png
new file mode 100644
index 0000000..d841821
--- /dev/null
+++ b/openpgp-api/src/main/res/drawable-mdpi/ic_action_cancel_launchersize_light.png
Binary files differ
diff --git a/openpgp-api/src/main/res/drawable-xhdpi/ic_action_cancel_launchersize.png b/openpgp-api/src/main/res/drawable-xhdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 0000000..1e3571f
--- /dev/null
+++ b/openpgp-api/src/main/res/drawable-xhdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/openpgp-api/src/main/res/drawable-xhdpi/ic_action_cancel_launchersize_light.png b/openpgp-api/src/main/res/drawable-xhdpi/ic_action_cancel_launchersize_light.png
new file mode 100644
index 0000000..d505046
--- /dev/null
+++ b/openpgp-api/src/main/res/drawable-xhdpi/ic_action_cancel_launchersize_light.png
Binary files differ
diff --git a/openpgp-api/src/main/res/drawable-xxhdpi/ic_action_cancel_launchersize.png b/openpgp-api/src/main/res/drawable-xxhdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 0000000..5204460
--- /dev/null
+++ b/openpgp-api/src/main/res/drawable-xxhdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/openpgp-api/src/main/res/drawable-xxhdpi/ic_action_cancel_launchersize_light.png b/openpgp-api/src/main/res/drawable-xxhdpi/ic_action_cancel_launchersize_light.png
new file mode 100644
index 0000000..d6fb86b
--- /dev/null
+++ b/openpgp-api/src/main/res/drawable-xxhdpi/ic_action_cancel_launchersize_light.png
Binary files differ
diff --git a/openpgp-api/src/main/res/values-cs/strings.xml b/openpgp-api/src/main/res/values-cs/strings.xml
new file mode 100644
index 0000000..c9fe1fa
--- /dev/null
+++ b/openpgp-api/src/main/res/values-cs/strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="openpgp_list_preference_none">Žádný</string>
+ <string name="openpgp_install_openkeychain_via">Instalovat OpenKeychain pomocí %s</string>
+</resources>
diff --git a/openpgp-api/src/main/res/values-de/strings.xml b/openpgp-api/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..91e800a
--- /dev/null
+++ b/openpgp-api/src/main/res/values-de/strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="openpgp_list_preference_none">Keine Auswahl</string>
+ <string name="openpgp_install_openkeychain_via">Installiere OpenKeychain mit %s</string>
+</resources>
diff --git a/openpgp-api/src/main/res/values-es/strings.xml b/openpgp-api/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000..da8979b
--- /dev/null
+++ b/openpgp-api/src/main/res/values-es/strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="openpgp_list_preference_none">Ninguno</string>
+ <string name="openpgp_install_openkeychain_via">Instalar OpenKeychain mediante %s</string>
+</resources>
diff --git a/openpgp-api/src/main/res/values-et/strings.xml b/openpgp-api/src/main/res/values-et/strings.xml
new file mode 100644
index 0000000..c757504
--- /dev/null
+++ b/openpgp-api/src/main/res/values-et/strings.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
diff --git a/openpgp-api/src/main/res/values-fi/strings.xml b/openpgp-api/src/main/res/values-fi/strings.xml
new file mode 100644
index 0000000..c757504
--- /dev/null
+++ b/openpgp-api/src/main/res/values-fi/strings.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
diff --git a/openpgp-api/src/main/res/values-fr/strings.xml b/openpgp-api/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000..9b36df2
--- /dev/null
+++ b/openpgp-api/src/main/res/values-fr/strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="openpgp_list_preference_none">Aucun</string>
+ <string name="openpgp_install_openkeychain_via">Installer OpenKeychain par %s</string>
+</resources>
diff --git a/openpgp-api/src/main/res/values-is/strings.xml b/openpgp-api/src/main/res/values-is/strings.xml
new file mode 100644
index 0000000..c757504
--- /dev/null
+++ b/openpgp-api/src/main/res/values-is/strings.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
diff --git a/openpgp-api/src/main/res/values-it/strings.xml b/openpgp-api/src/main/res/values-it/strings.xml
new file mode 100644
index 0000000..23e8e80
--- /dev/null
+++ b/openpgp-api/src/main/res/values-it/strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="openpgp_list_preference_none">Nessuno</string>
+ <string name="openpgp_install_openkeychain_via">Installa OpenKeychain via %s</string>
+</resources>
diff --git a/openpgp-api/src/main/res/values-ja/strings.xml b/openpgp-api/src/main/res/values-ja/strings.xml
new file mode 100644
index 0000000..5e337f5
--- /dev/null
+++ b/openpgp-api/src/main/res/values-ja/strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="openpgp_list_preference_none">無し</string>
+ <string name="openpgp_install_openkeychain_via">%s 経由でOpenKeychainをインストール</string>
+</resources>
diff --git a/openpgp-api/src/main/res/values-nl/strings.xml b/openpgp-api/src/main/res/values-nl/strings.xml
new file mode 100644
index 0000000..c757504
--- /dev/null
+++ b/openpgp-api/src/main/res/values-nl/strings.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
diff --git a/openpgp-api/src/main/res/values-pl/strings.xml b/openpgp-api/src/main/res/values-pl/strings.xml
new file mode 100644
index 0000000..c757504
--- /dev/null
+++ b/openpgp-api/src/main/res/values-pl/strings.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
diff --git a/openpgp-api/src/main/res/values-pt/strings.xml b/openpgp-api/src/main/res/values-pt/strings.xml
new file mode 100644
index 0000000..c757504
--- /dev/null
+++ b/openpgp-api/src/main/res/values-pt/strings.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
diff --git a/openpgp-api/src/main/res/values-ru/strings.xml b/openpgp-api/src/main/res/values-ru/strings.xml
new file mode 100644
index 0000000..e8fd1dd
--- /dev/null
+++ b/openpgp-api/src/main/res/values-ru/strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="openpgp_list_preference_none">Нет</string>
+ <string name="openpgp_install_openkeychain_via">Установить OpenKeychain через %s</string>
+</resources>
diff --git a/openpgp-api/src/main/res/values-sl/strings.xml b/openpgp-api/src/main/res/values-sl/strings.xml
new file mode 100644
index 0000000..20bf70b
--- /dev/null
+++ b/openpgp-api/src/main/res/values-sl/strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="openpgp_list_preference_none">Brez</string>
+ <string name="openpgp_install_openkeychain_via">Namesti OpenKeychain prek %s</string>
+</resources>
diff --git a/openpgp-api/src/main/res/values-tr/strings.xml b/openpgp-api/src/main/res/values-tr/strings.xml
new file mode 100644
index 0000000..c757504
--- /dev/null
+++ b/openpgp-api/src/main/res/values-tr/strings.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
diff --git a/openpgp-api/src/main/res/values-uk/strings.xml b/openpgp-api/src/main/res/values-uk/strings.xml
new file mode 100644
index 0000000..baf600a
--- /dev/null
+++ b/openpgp-api/src/main/res/values-uk/strings.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="openpgp_list_preference_none">Жоден</string>
+ <string name="openpgp_install_openkeychain_via">Встановити OpenKeychain через %s</string>
+</resources>
diff --git a/openpgp-api/src/main/res/values-zh/strings.xml b/openpgp-api/src/main/res/values-zh/strings.xml
new file mode 100644
index 0000000..c757504
--- /dev/null
+++ b/openpgp-api/src/main/res/values-zh/strings.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
diff --git a/openpgp-api/src/main/res/values/strings.xml b/openpgp-api/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a45524f
--- /dev/null
+++ b/openpgp-api/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="openpgp_list_preference_none">None</string>
+ <string name="openpgp_install_openkeychain_via">Install OpenKeychain via %s</string>
+ <string name="openpgp_no_key_selected">No key selected</string>
+ <string name="openpgp_key_selected">Key has been selected</string>
+
+</resources> \ No newline at end of file