aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore29
-rw-r--r--AndroidManifest.xml13
-rw-r--r--LICENSE202
-rw-r--r--build.gradle35
-rw-r--r--build.xml92
-rw-r--r--proguard-project.txt20
-rw-r--r--project.properties15
-rw-r--r--res/drawable-hdpi/ic_action_cancel_launchersize.pngbin0 -> 1520 bytes
-rw-r--r--res/drawable-hdpi/ic_action_cancel_launchersize_light.pngbin0 -> 1940 bytes
-rw-r--r--res/drawable-mdpi/ic_action_cancel_launchersize.pngbin0 -> 1032 bytes
-rw-r--r--res/drawable-mdpi/ic_action_cancel_launchersize_light.pngbin0 -> 1098 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_cancel_launchersize.pngbin0 -> 1570 bytes
-rw-r--r--res/drawable-xhdpi/ic_action_cancel_launchersize_light.pngbin0 -> 2039 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_cancel_launchersize.pngbin0 -> 2345 bytes
-rw-r--r--res/drawable-xxhdpi/ic_action_cancel_launchersize_light.pngbin0 -> 2404 bytes
-rw-r--r--res/values/strings.xml7
-rw-r--r--src/org/openintents/openpgp/IOpenPgpService.aidl24
-rw-r--r--src/org/openintents/openpgp/OpenPgpError.java118
-rw-r--r--src/org/openintents/openpgp/OpenPgpSignatureResult.java163
-rw-r--r--src/org/openintents/openpgp/util/OpenPgpApi.java270
-rw-r--r--src/org/openintents/openpgp/util/OpenPgpListPreference.java257
-rw-r--r--src/org/openintents/openpgp/util/OpenPgpServiceConnection.java118
-rw-r--r--src/org/openintents/openpgp/util/OpenPgpUtils.java76
-rw-r--r--src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java103
24 files changed, 1542 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..aa8bb576
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+#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
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 00000000..98cb89fa
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.openintents.openpgp"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="19" />
+
+ <application/>
+
+</manifest> \ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..69b937c1
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,35 @@
+// please leave this here, so this library builds on its own
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.10.0'
+ }
+}
+
+apply plugin: 'android-library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion '19.0.3'
+
+ // NOTE: We are using the old folder structure to also support Eclipse
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ resources.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ assets.srcDirs = ['assets']
+ }
+ }
+
+ // Do not abort build if lint finds errors
+ lintOptions {
+ abortOnError false
+ }
+}
diff --git a/build.xml b/build.xml
new file mode 100644
index 00000000..48ebf198
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="keychain-api-library" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var.
+ This must be done before we load project.properties since
+ the proguard config can use sdk.dir -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/proguard-project.txt b/proguard-project.txt
new file mode 100644
index 00000000..f2fe1559
--- /dev/null
+++ b/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/project.properties b/project.properties
new file mode 100644
index 00000000..91d2b024
--- /dev/null
+++ b/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
diff --git a/res/drawable-hdpi/ic_action_cancel_launchersize.png b/res/drawable-hdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 00000000..71b9118d
--- /dev/null
+++ b/res/drawable-hdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_cancel_launchersize_light.png b/res/drawable-hdpi/ic_action_cancel_launchersize_light.png
new file mode 100644
index 00000000..73b1d08f
--- /dev/null
+++ b/res/drawable-hdpi/ic_action_cancel_launchersize_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_cancel_launchersize.png b/res/drawable-mdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 00000000..270abf45
--- /dev/null
+++ b/res/drawable-mdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_cancel_launchersize_light.png b/res/drawable-mdpi/ic_action_cancel_launchersize_light.png
new file mode 100644
index 00000000..d841821c
--- /dev/null
+++ b/res/drawable-mdpi/ic_action_cancel_launchersize_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_cancel_launchersize.png b/res/drawable-xhdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 00000000..1e3571fa
--- /dev/null
+++ b/res/drawable-xhdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_cancel_launchersize_light.png b/res/drawable-xhdpi/ic_action_cancel_launchersize_light.png
new file mode 100644
index 00000000..d505046b
--- /dev/null
+++ b/res/drawable-xhdpi/ic_action_cancel_launchersize_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_cancel_launchersize.png b/res/drawable-xxhdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 00000000..52044601
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_action_cancel_launchersize_light.png b/res/drawable-xxhdpi/ic_action_cancel_launchersize_light.png
new file mode 100644
index 00000000..d6fb86bd
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_action_cancel_launchersize_light.png
Binary files differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 00000000..0119831c
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,7 @@
+<?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>
+
+</resources> \ No newline at end of file
diff --git a/src/org/openintents/openpgp/IOpenPgpService.aidl b/src/org/openintents/openpgp/IOpenPgpService.aidl
new file mode 100644
index 00000000..7ee79d6a
--- /dev/null
+++ b/src/org/openintents/openpgp/IOpenPgpService.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 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/src/org/openintents/openpgp/OpenPgpError.java b/src/org/openintents/openpgp/OpenPgpError.java
new file mode 100644
index 00000000..b894a460
--- /dev/null
+++ b/src/org/openintents/openpgp/OpenPgpError.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 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/src/org/openintents/openpgp/OpenPgpSignatureResult.java b/src/org/openintents/openpgp/OpenPgpSignatureResult.java
new file mode 100644
index 00000000..7a4d799d
--- /dev/null
+++ b/src/org/openintents/openpgp/OpenPgpSignatureResult.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 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.Locale;
+
+/**
+ * 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 = 1;
+
+ // generic error on signature verification
+ public static final int SIGNATURE_ERROR = 0;
+ // successfully verified signature, with certified public key
+ public static final int SIGNATURE_SUCCESS_CERTIFIED = 1;
+ // no public key was found for this signature verification
+ public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2;
+ // successfully verified signature, but with uncertified public key
+ public static final int SIGNATURE_SUCCESS_UNCERTIFIED = 3;
+
+ int status;
+ boolean signatureOnly;
+ String userId;
+ 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 getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ 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) {
+ this.status = signatureStatus;
+ this.signatureOnly = signatureOnly;
+ this.userId = signatureUserId;
+ this.keyId = keyId;
+ }
+
+ public OpenPgpSignatureResult(OpenPgpSignatureResult b) {
+ this.status = b.status;
+ this.userId = b.userId;
+ this.signatureOnly = b.signatureOnly;
+ this.keyId = b.keyId;
+ }
+
+ 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(userId);
+ dest.writeLong(keyId);
+ // 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.userId = source.readString();
+ vr.keyId = source.readLong();
+
+ // 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 = new String();
+ out += "\nstatus: " + status;
+ out += "\nuserId: " + userId;
+ out += "\nsignatureOnly: " + signatureOnly;
+ out += "\nkeyId: " + OpenPgpUtils.convertKeyIdToHex(keyId);
+ return out;
+ }
+
+}
diff --git a/src/org/openintents/openpgp/util/OpenPgpApi.java b/src/org/openintents/openpgp/util/OpenPgpApi.java
new file mode 100644
index 00000000..f6a78d0e
--- /dev/null
+++ b/src/org/openintents/openpgp/util/OpenPgpApi.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2014 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.InputStream;
+import java.io.OutputStream;
+
+public class OpenPgpApi {
+
+ public static final String TAG = "OpenPgp API";
+
+ public static final int API_VERSION = 3;
+ public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
+
+ /**
+ * 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)
+ */
+
+ /**
+ * Sign only
+ * <p/>
+ * optional extras:
+ * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
+ * String EXTRA_PASSPHRASE (key passphrase)
+ */
+ public static final String ACTION_SIGN = "org.openintents.openpgp.action.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 ouput)
+ * String EXTRA_PASSPHRASE (key passphrase)
+ */
+ 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:
+ * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
+ * String EXTRA_PASSPHRASE (key passphrase)
+ */
+ 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.
+ * <p/>
+ * If OpenPgpSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY
+ * in addition a PendingIntent is returned via RESULT_INTENT to download missing keys.
+ * <p/>
+ * optional extras:
+ * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
+ * <p/>
+ * returned extras:
+ * OpenPgpSignatureResult RESULT_SIGNATURE
+ */
+ public static final String ACTION_DECRYPT_VERIFY = "org.openintents.openpgp.action.DECRYPT_VERIFY";
+
+ /**
+ * 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";
+
+ public static final String EXTRA_ACCOUNT_NAME = "account_name";
+
+ // 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";
+
+ // ENCRYPT, SIGN_AND_ENCRYPT
+ public static final String EXTRA_USER_IDS = "user_ids";
+ public static final String EXTRA_KEY_IDS = "key_ids";
+ // optional extras:
+ public static final String EXTRA_PASSPHRASE = "passphrase";
+
+ // 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 RESULT_SIGNATURE = "signature";
+
+ 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);
+ }
+ }
+
+ public Intent executeApi(Intent data, InputStream is, OutputStream os) {
+ try {
+ data.putExtra(EXTRA_API_VERSION, OpenPgpApi.API_VERSION);
+
+ Intent result;
+
+ // pipe the input and output
+ ParcelFileDescriptor input = null;
+ if (is != null) {
+ input = ParcelFileDescriptorUtil.pipeFrom(is,
+ new ParcelFileDescriptorUtil.IThreadListener() {
+
+ @Override
+ public void onThreadFinished(Thread thread) {
+ //Log.d(OpenPgpApi.TAG, "Copy to service finished");
+ }
+ }
+ );
+ }
+ ParcelFileDescriptor output = null;
+ 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);
+ // close() is required to halt the TransferThread
+ if (output != null) {
+ output.close();
+ }
+ // TODO: close input?
+
+ // 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;
+ }
+ }
+
+}
diff --git a/src/org/openintents/openpgp/util/OpenPgpListPreference.java b/src/org/openintents/openpgp/util/OpenPgpListPreference.java
new file mode 100644
index 00000000..cf586462
--- /dev/null
+++ b/src/org/openintents/openpgp/util/OpenPgpListPreference.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2014 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.util.AttributeSet;
+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 OpenPgpListPreference 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 ArrayList<OpenPgpProviderEntry> mLegacyList = new ArrayList<OpenPgpProviderEntry>();
+ private ArrayList<OpenPgpProviderEntry> mList = new ArrayList<OpenPgpProviderEntry>();
+
+ private String mSelectedPackage;
+
+ public OpenPgpListPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public OpenPgpListPreference(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) {
+ 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<OpenPgpProviderEntry>();
+ 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());
+
+ 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);
+ }
+
+ // 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(getValue()),
+ 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);
+ }
+
+ mSelectedPackage = entry.packageName;
+
+ /*
+ * Clicking on an item simulates the positive button click, and dismisses
+ * the dialog.
+ */
+ OpenPgpListPreference.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)) {
+ if (callChangeListener(mSelectedPackage)) {
+ setValue(mSelectedPackage);
+ }
+ }
+ }
+
+ private int getIndexOfProviderList(String packageName) {
+ for (OpenPgpProviderEntry app : mList) {
+ if (app.packageName.equals(packageName)) {
+ return mList.indexOf(app);
+ }
+ }
+
+ return -1;
+ }
+
+ public void setValue(String packageName) {
+ mSelectedPackage = packageName;
+ persistString(packageName);
+ }
+
+ public String getValue() {
+ return mSelectedPackage;
+ }
+
+ public String getEntry() {
+ return getEntryByValue(mSelectedPackage);
+ }
+
+ @Override
+ protected Object onGetDefaultValue(TypedArray a, int index) {
+ return a.getString(index);
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+ setValue(restoreValue ? getPersistedString(mSelectedPackage) : (String) defaultValue);
+ }
+
+ public String getEntryByValue(String packageName) {
+ for (OpenPgpProviderEntry app : mList) {
+ if (app.packageName.equals(packageName)) {
+ return app.simpleName;
+ }
+ }
+
+ return null;
+ }
+
+ 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/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java b/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java
new file mode 100644
index 00000000..0395a7bc
--- /dev/null
+++ b/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 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 org.openintents.openpgp.IOpenPgpService;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+public class OpenPgpServiceConnection {
+
+ // interface to create callbacks for onServiceConnected
+ public interface OnBound {
+ public void onBound(IOpenPgpService service);
+ }
+
+ private Context mApplicationContext;
+
+ private IOpenPgpService mService;
+ private String mProviderPackageName;
+
+ private OnBound mOnBoundListener;
+
+ /**
+ * Create new OpenPgpServiceConnection
+ *
+ * @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 OpenPgpServiceConnection
+ *
+ * @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.mApplicationContext = context.getApplicationContext();
+ this.mProviderPackageName = 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 boolean bindToService() {
+ // if not already bound...
+ if (mService == null) {
+ try {
+ Intent serviceIntent = new Intent();
+ serviceIntent.setAction(IOpenPgpService.class.getName());
+ // NOTE: setPackage is very important to restrict the intent to this provider only!
+ serviceIntent.setPackage(mProviderPackageName);
+ mApplicationContext.bindService(serviceIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE);
+
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public void unbindFromService() {
+ mApplicationContext.unbindService(mServiceConnection);
+ }
+
+}
diff --git a/src/org/openintents/openpgp/util/OpenPgpUtils.java b/src/org/openintents/openpgp/util/OpenPgpUtils.java
new file mode 100644
index 00000000..e24c937a
--- /dev/null
+++ b/src/org/openintents/openpgp/util/OpenPgpUtils.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 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;
+
+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);
+ if (!resInfo.isEmpty()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ 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.US);
+ while (hexString.length() < 8) {
+ hexString = "0" + hexString;
+ }
+ return hexString;
+ }
+}
diff --git a/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java b/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java
new file mode 100644
index 00000000..58c62110
--- /dev/null
+++ b/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * 2013 Flow (http://stackoverflow.com/questions/18212152/transfer-inputstream-to-another-service-across-process-boundaries-with-parcelf)
+ *
+ * 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;
+
+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);
+ }
+ }
+ }
+} \ No newline at end of file