Merge branch 'master' into certs
Conflicts: OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@ -10,7 +10,7 @@ before_install:
|
|||||||
- export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
|
- export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
|
||||||
|
|
||||||
# Install required Android components.
|
# Install required Android components.
|
||||||
- echo "y" | android update sdk -a --filter build-tools-19.0.1,android-19,platform-tools,extra-android-support,extra-android-m2repository,android-17 --no-ui --force
|
- echo "y" | android update sdk -a --filter build-tools-19.0.3,android-19,platform-tools,extra-android-support,extra-android-m2repository --no-ui --force
|
||||||
install: echo "Installation done"
|
install: echo "Installation done"
|
||||||
script: gradle assemble -S -q
|
script: gradle assemble -S -q
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
task wrapper(type: Wrapper) {
|
task wrapper(type: Wrapper) {
|
||||||
gradleVersion = '1.10'
|
gradleVersion = '1.10'
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:0.8.3'
|
classpath 'com.android.tools.build:gradle:0.9.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,12 +13,13 @@ apply plugin: 'android'
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.android.support:support-v4:19.0.1'
|
compile 'com.android.support:support-v4:19.0.1'
|
||||||
compile project(':libraries:keychain-api-library')
|
compile project(':libraries:openpgp-api-library')
|
||||||
|
compile project(':libraries:openkeychain-api-library')
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 19
|
compileSdkVersion 19
|
||||||
buildToolsVersion "19.0.1"
|
buildToolsVersion "19.0.3"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 9
|
minSdkVersion 9
|
||||||
|
@ -202,7 +202,7 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpenPgpApi.RESULT_CODE_ERROR: {
|
case OpenPgpApi.RESULT_CODE_ERROR: {
|
||||||
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERRORS);
|
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
|
||||||
handleError(error);
|
handleError(error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -234,7 +234,7 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void signAndEncrypt(Intent data) {
|
public void signAndEncrypt(Intent data) {
|
||||||
data.setAction(OpenPgpApi.ACTION_SIGN_AND_ENCTYPT);
|
data.setAction(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT);
|
||||||
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#Fri Feb 14 01:26:40 CET 2014
|
#Thu Mar 06 22:23:44 CET 2014
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
|
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
|
||||||
|
@ -5,7 +5,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:0.8.3'
|
classpath 'com.android.tools.build:gradle:0.9.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ apply plugin: 'android-library'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 19
|
compileSdkVersion 19
|
||||||
buildToolsVersion '19.0.1'
|
buildToolsVersion '19.0.3'
|
||||||
|
|
||||||
// NOTE: We are using the old folder structure to also support Eclipse
|
// NOTE: We are using the old folder structure to also support Eclipse
|
||||||
sourceSets {
|
sourceSets {
|
@ -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>
|
@ -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 *;
|
||||||
|
#}
|
29
OpenPGP-Keychain-API/libraries/openpgp-api-library/.gitignore
vendored
Normal file
@ -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
|
@ -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>
|
202
OpenPGP-Keychain-API/libraries/openpgp-api-library/LICENSE
Normal file
@ -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.
|
@ -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.9.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
|
||||||
|
}
|
||||||
|
}
|
92
OpenPGP-Keychain-API/libraries/openpgp-api-library/build.xml
Normal file
@ -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>
|
@ -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 *;
|
||||||
|
#}
|
@ -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
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
@ -38,24 +38,40 @@ public class OpenPgpSignatureResult implements Parcelable {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setStatus(int status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSignatureOnly() {
|
public boolean isSignatureOnly() {
|
||||||
return signatureOnly;
|
return signatureOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSignatureOnly(boolean signatureOnly) {
|
||||||
|
this.signatureOnly = signatureOnly;
|
||||||
|
}
|
||||||
|
|
||||||
public String getUserId() {
|
public String getUserId() {
|
||||||
return userId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
public long getKeyId() {
|
public long getKeyId() {
|
||||||
return keyId;
|
return keyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setKeyId(long keyId) {
|
||||||
|
this.keyId = keyId;
|
||||||
|
}
|
||||||
|
|
||||||
public OpenPgpSignatureResult() {
|
public OpenPgpSignatureResult() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpSignatureResult(int signatureStatus, String signatureUserId,
|
public OpenPgpSignatureResult(int signatureStatus, String signatureUserId,
|
||||||
boolean signatureOnly, long keyId) {
|
boolean signatureOnly, long keyId) {
|
||||||
this.status = signatureStatus;
|
this.status = signatureStatus;
|
||||||
this.signatureOnly = signatureOnly;
|
this.signatureOnly = signatureOnly;
|
||||||
this.userId = signatureUserId;
|
this.userId = signatureUserId;
|
@ -16,119 +16,146 @@
|
|||||||
|
|
||||||
package org.openintents.openpgp.util;
|
package org.openintents.openpgp.util;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.openintents.openpgp.IOpenPgpService;
|
import org.openintents.openpgp.IOpenPgpService;
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
import org.openintents.openpgp.OpenPgpError;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public class OpenPgpApi {
|
public class OpenPgpApi {
|
||||||
|
|
||||||
//TODO: fix this documentation
|
public static final String TAG = "OpenPgp API";
|
||||||
|
|
||||||
|
public static final int API_VERSION = 2;
|
||||||
|
public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General extras
|
* General extras
|
||||||
* --------------
|
* --------------
|
||||||
*
|
*
|
||||||
* Intent extras:
|
* required extras:
|
||||||
* int api_version (required)
|
* int EXTRA_API_VERSION (always required)
|
||||||
* boolean ascii_armor (request ascii armor for ouput)
|
|
||||||
*
|
*
|
||||||
* returned Bundle:
|
* returned extras:
|
||||||
* int result_code (0, 1, or 2 (see OpenPgpApi))
|
* int RESULT_CODE (RESULT_CODE_ERROR, RESULT_CODE_SUCCESS or RESULT_CODE_USER_INTERACTION_REQUIRED)
|
||||||
* OpenPgpError error (if result_code == 0)
|
* OpenPgpError RESULT_ERROR (if RESULT_CODE == RESULT_CODE_ERROR)
|
||||||
* Intent intent (if result_code == 2)
|
* PendingIntent RESULT_INTENT (if RESULT_CODE == RESULT_CODE_USER_INTERACTION_REQUIRED)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign only
|
* Sign only
|
||||||
*
|
*
|
||||||
* optional params:
|
* optional extras:
|
||||||
* String passphrase (for key passphrase)
|
* 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
|
* Encrypt
|
||||||
*
|
*
|
||||||
* Intent extras:
|
* required extras:
|
||||||
* long[] key_ids
|
* String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT)
|
||||||
* or
|
* or
|
||||||
* String[] user_ids (= emails of recipients) (if more than one key has this user_id, a PendingIntent is returned)
|
* long[] EXTRA_KEY_IDS
|
||||||
*
|
*
|
||||||
* optional extras:
|
* optional extras:
|
||||||
* String passphrase (for key passphrase)
|
* 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
|
* Sign and encrypt
|
||||||
*
|
*
|
||||||
* Intent extras:
|
* required extras:
|
||||||
* same as in encrypt()
|
* 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
|
||||||
|
*
|
||||||
|
* 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 bytes. This methods handles encrypted-only, signed-and-encrypted,
|
* Decrypts and verifies given input stream. This methods handles encrypted-only, signed-and-encrypted,
|
||||||
* and also signed-only input.
|
* and also signed-only input.
|
||||||
*
|
*
|
||||||
* returned Bundle:
|
* If OpenPgpSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY
|
||||||
* OpenPgpSignatureResult signature_result
|
* in addition a PendingIntent is returned via RESULT_INTENT to download missing keys.
|
||||||
|
*
|
||||||
|
* optional extras:
|
||||||
|
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
|
||||||
|
*
|
||||||
|
* returned extras:
|
||||||
|
* OpenPgpSignatureResult RESULT_SIGNATURE
|
||||||
*/
|
*/
|
||||||
|
public static final String ACTION_DECRYPT_VERIFY = "org.openintents.openpgp.action.DECRYPT_VERIFY";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves key ids based on given user ids (=emails)
|
* Get key ids based on given user ids (=emails)
|
||||||
*
|
*
|
||||||
* Intent extras:
|
* required extras:
|
||||||
* String[] user_ids
|
* String[] EXTRA_USER_IDS
|
||||||
*
|
*
|
||||||
* returned Bundle:
|
* returned extras:
|
||||||
* long[] key_ids
|
* long[] EXTRA_KEY_IDS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static final String TAG = "OpenPgp API";
|
|
||||||
|
|
||||||
public static final int API_VERSION = 2;
|
|
||||||
public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
|
|
||||||
|
|
||||||
public static final String ACTION_SIGN = "org.openintents.openpgp.action.SIGN";
|
|
||||||
public static final String ACTION_ENCRYPT = "org.openintents.openpgp.action.ENCRYPT";
|
|
||||||
public static final String ACTION_SIGN_AND_ENCTYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT";
|
|
||||||
public static final String ACTION_DECRYPT_VERIFY = "org.openintents.openpgp.action.DECRYPT_VERIFY";
|
|
||||||
public static final String ACTION_DOWNLOAD_KEYS = "org.openintents.openpgp.action.DOWNLOAD_KEYS";
|
|
||||||
public static final String ACTION_GET_KEY_IDS = "org.openintents.openpgp.action.GET_KEY_IDS";
|
public static final String ACTION_GET_KEY_IDS = "org.openintents.openpgp.action.GET_KEY_IDS";
|
||||||
|
|
||||||
/* Bundle params */
|
/**
|
||||||
|
* This action returns RESULT_CODE_SUCCESS if the OpenPGP Provider already has the key
|
||||||
|
* corresponding to the given key id in its database.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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_API_VERSION = "api_version";
|
||||||
|
|
||||||
// SIGN, ENCRYPT, SIGN_ENCRYPT, DECRYPT_VERIFY
|
// SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY
|
||||||
// request ASCII Armor for output
|
// request ASCII Armor for output
|
||||||
// OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
|
// 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";
|
public static final String EXTRA_REQUEST_ASCII_ARMOR = "ascii_armor";
|
||||||
|
|
||||||
// ENCRYPT, SIGN_ENCRYPT
|
// ENCRYPT, SIGN_AND_ENCRYPT
|
||||||
public static final String EXTRA_USER_IDS = "user_ids";
|
public static final String EXTRA_USER_IDS = "user_ids";
|
||||||
public static final String EXTRA_KEY_IDS = "key_ids";
|
public static final String EXTRA_KEY_IDS = "key_ids";
|
||||||
// optional parameter:
|
// optional extras:
|
||||||
public static final String EXTRA_PASSPHRASE = "passphrase";
|
public static final String EXTRA_PASSPHRASE = "passphrase";
|
||||||
|
|
||||||
/* Service Bundle returns */
|
// GET_KEY
|
||||||
public static final String RESULT_CODE = "result_code";
|
public static final String EXTRA_KEY_ID = "key_id";
|
||||||
public static final String RESULT_SIGNATURE = "signature";
|
|
||||||
public static final String RESULT_ERRORS = "error";
|
|
||||||
public static final String RESULT_INTENT = "intent";
|
|
||||||
|
|
||||||
// get actual error object from RESULT_ERRORS
|
/* 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;
|
public static final int RESULT_CODE_ERROR = 0;
|
||||||
// success!
|
// success!
|
||||||
public static final int RESULT_CODE_SUCCESS = 1;
|
public static final int RESULT_CODE_SUCCESS = 1;
|
||||||
// executeServiceMethod intent and do it again with intent
|
// 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 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;
|
IOpenPgpService mService;
|
||||||
Context mContext;
|
Context mContext;
|
||||||
@ -166,6 +193,7 @@ public class OpenPgpApi {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
public void executeApiAsync(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
public void executeApiAsync(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
||||||
OpenPgpAsyncTask task = new OpenPgpAsyncTask(data, is, os, callback);
|
OpenPgpAsyncTask task = new OpenPgpAsyncTask(data, is, os, callback);
|
||||||
|
|
||||||
@ -188,13 +216,13 @@ public class OpenPgpApi {
|
|||||||
result = mService.execute(data, null, null);
|
result = mService.execute(data, null, null);
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
// send the input and output pfds
|
// pipe the input and output
|
||||||
ParcelFileDescriptor input = ParcelFileDescriptorUtil.pipeFrom(is,
|
ParcelFileDescriptor input = ParcelFileDescriptorUtil.pipeFrom(is,
|
||||||
new ParcelFileDescriptorUtil.IThreadListener() {
|
new ParcelFileDescriptorUtil.IThreadListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onThreadFinished(Thread thread) {
|
public void onThreadFinished(Thread thread) {
|
||||||
Log.d(OpenPgpApi.TAG, "Copy to service finished");
|
//Log.d(OpenPgpApi.TAG, "Copy to service finished");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ParcelFileDescriptor output = ParcelFileDescriptorUtil.pipeTo(os,
|
ParcelFileDescriptor output = ParcelFileDescriptorUtil.pipeTo(os,
|
||||||
@ -202,7 +230,7 @@ public class OpenPgpApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onThreadFinished(Thread thread) {
|
public void onThreadFinished(Thread thread) {
|
||||||
Log.d(OpenPgpApi.TAG, "Service finished writing!");
|
//Log.d(OpenPgpApi.TAG, "Service finished writing!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -222,7 +250,7 @@ public class OpenPgpApi {
|
|||||||
Log.e(OpenPgpApi.TAG, "Exception", e);
|
Log.e(OpenPgpApi.TAG, "Exception", e);
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(RESULT_CODE, RESULT_CODE_ERROR);
|
result.putExtra(RESULT_CODE, RESULT_CODE_ERROR);
|
||||||
result.putExtra(RESULT_ERRORS,
|
result.putExtra(RESULT_ERROR,
|
||||||
new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage()));
|
new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
@ -31,7 +31,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.ListAdapter;
|
import android.widget.ListAdapter;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import org.sufficientlysecure.keychain.api.R;
|
import org.openintents.openpgp.R;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
@ -1,2 +1,3 @@
|
|||||||
include ':example-app'
|
include ':example-app'
|
||||||
include ':libraries:keychain-api-library'
|
include ':libraries:openpgp-api-library'
|
||||||
|
include ':libraries:openkeychain-api-library'
|
@ -3,7 +3,8 @@ apply plugin: 'android'
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.android.support:support-v4:19.0.1'
|
compile 'com.android.support:support-v4:19.0.1'
|
||||||
compile 'com.android.support:appcompat-v7:19.0.1'
|
compile 'com.android.support:appcompat-v7:19.0.1'
|
||||||
compile project(':OpenPGP-Keychain-API:libraries:keychain-api-library')
|
compile project(':OpenPGP-Keychain-API:libraries:openpgp-api-library')
|
||||||
|
compile project(':OpenPGP-Keychain-API:libraries:openkeychain-api-library')
|
||||||
compile project(':libraries:HtmlTextView')
|
compile project(':libraries:HtmlTextView')
|
||||||
compile project(':libraries:StickyListHeaders:library')
|
compile project(':libraries:StickyListHeaders:library')
|
||||||
compile project(':libraries:AndroidBootstrap')
|
compile project(':libraries:AndroidBootstrap')
|
||||||
@ -18,7 +19,7 @@ dependencies {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 19
|
compileSdkVersion 19
|
||||||
buildToolsVersion "19.0.1"
|
buildToolsVersion "19.0.3"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 9
|
minSdkVersion 9
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.sufficientlysecure.keychain"
|
package="org.sufficientlysecure.keychain"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="23101"
|
android:versionCode="23104"
|
||||||
android:versionName="2.3.1 beta1">
|
android:versionName="2.3.1 beta4">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
General remarks
|
General remarks
|
||||||
@ -22,7 +22,8 @@
|
|||||||
Remarks about the ugly android:pathPattern:
|
Remarks about the ugly android:pathPattern:
|
||||||
- We are matching all files with a specific file ending.
|
- We are matching all files with a specific file ending.
|
||||||
This is done in an ugly way because of Android limitations.
|
This is done in an ugly way because of Android limitations.
|
||||||
Read http://stackoverflow.com/questions/1733195/android-intent-filter-for-a-particular-file-extension and http://stackoverflow.com/questions/3400072/pathpattern-to-match-file-extension-does-not-work-if-a-period-exists-elsewhere-i/8599921
|
Read http://stackoverflow.com/questions/1733195/android-intent-filter-for-a-particular-file-extension
|
||||||
|
and http://stackoverflow.com/questions/3400072/pathpattern-to-match-file-extension-does-not-work-if-a-period-exists-elsewhere-i/8599921
|
||||||
for more information.
|
for more information.
|
||||||
- Do _not_ set mimeType for gpg!
|
- Do _not_ set mimeType for gpg!
|
||||||
Cyanogenmod's file manager will only show Keychain for gpg files if no mimeType is set!
|
Cyanogenmod's file manager will only show Keychain for gpg files if no mimeType is set!
|
||||||
@ -49,6 +50,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
|
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
|
||||||
|
|
||||||
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
|
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
|
||||||
<application
|
<application
|
||||||
@ -106,30 +108,12 @@
|
|||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_select_recipients"
|
android:label="@string/title_select_recipients"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop">
|
||||||
|
|
||||||
<!-- <intent-filter> -->
|
|
||||||
<!-- <action android:name="android.intent.action.SEARCH" /> -->
|
|
||||||
<!-- </intent-filter> -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <meta-data -->
|
|
||||||
<!-- android:name="android.app.searchable" -->
|
|
||||||
<!-- android:resource="@xml/searchable_public_keys" /> -->
|
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.SelectSecretKeyActivity"
|
android:name=".ui.SelectSecretKeyActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_select_secret_key"
|
android:label="@string/title_select_secret_key"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop">
|
||||||
|
|
||||||
<!-- <intent-filter> -->
|
|
||||||
<!-- <action android:name="android.intent.action.SEARCH" /> -->
|
|
||||||
<!-- </intent-filter> -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <meta-data -->
|
|
||||||
<!-- android:name="android.app.searchable" -->
|
|
||||||
<!-- android:resource="@xml/searchable_secret_keys" /> -->
|
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.EncryptActivity"
|
android:name=".ui.EncryptActivity"
|
||||||
@ -261,7 +245,16 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.PreferencesActivity"
|
android:name=".ui.PreferencesActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_preferences" />
|
android:label="@string/title_preferences" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.sufficientlysecure.keychain.ui.PREFS_GEN" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.sufficientlysecure.keychain.ui.PREFS_ADV" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.PreferencesKeyServerActivity"
|
android:name=".ui.PreferencesKeyServerActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
@ -439,10 +432,6 @@
|
|||||||
<!--<intent-filter>-->
|
<!--<intent-filter>-->
|
||||||
<!--<action android:name="org.sufficientlysecure.keychain.service.remote.IExtendedApiService" />-->
|
<!--<action android:name="org.sufficientlysecure.keychain.service.remote.IExtendedApiService" />-->
|
||||||
<!--</intent-filter>-->
|
<!--</intent-filter>-->
|
||||||
|
|
||||||
<!--<meta-data-->
|
|
||||||
<!--android:name="api_version"-->
|
|
||||||
<!--android:value="1" />-->
|
|
||||||
<!--</service>-->
|
<!--</service>-->
|
||||||
|
|
||||||
<!-- TODO: authority! Make this API with content provider uris -->
|
<!-- TODO: authority! Make this API with content provider uris -->
|
||||||
@ -452,4 +441,4 @@
|
|||||||
<!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API" /> -->
|
<!-- android:permission="org.sufficientlysecure.keychain.permission.ACCESS_API" /> -->
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -43,6 +43,8 @@ public final class Constants {
|
|||||||
public static final class path {
|
public static final class path {
|
||||||
public static final String APP_DIR = Environment.getExternalStorageDirectory()
|
public static final String APP_DIR = Environment.getExternalStorageDirectory()
|
||||||
+ "/OpenPGP-Keychain";
|
+ "/OpenPGP-Keychain";
|
||||||
|
public static final String APP_DIR_FILE_SEC = APP_DIR + "/secexport.asc";
|
||||||
|
public static final String APP_DIR_FILE_PUB = APP_DIR + "/pubexport.asc";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class pref {
|
public static final class pref {
|
||||||
@ -51,7 +53,7 @@ public final class Constants {
|
|||||||
public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour";
|
public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour";
|
||||||
public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression";
|
public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression";
|
||||||
public static final String DEFAULT_FILE_COMPRESSION = "defaultFileCompression";
|
public static final String DEFAULT_FILE_COMPRESSION = "defaultFileCompression";
|
||||||
public static final String PASS_PHRASE_CACHE_TTL = "passPhraseCacheTtl";
|
public static final String PASS_PHRASE_CACHE_TTL = "passphraseCacheTtl";
|
||||||
public static final String LANGUAGE = "language";
|
public static final String LANGUAGE = "language";
|
||||||
public static final String FORCE_V3_SIGNATURES = "forceV3Signatures";
|
public static final String FORCE_V3_SIGNATURES = "forceV3Signatures";
|
||||||
public static final String KEY_SERVERS = "keyServers";
|
public static final String KEY_SERVERS = "keyServers";
|
||||||
|
@ -30,7 +30,7 @@ public final class Id {
|
|||||||
public static final class menu {
|
public static final class menu {
|
||||||
|
|
||||||
public static final class option {
|
public static final class option {
|
||||||
public static final int new_pass_phrase = 0x21070001;
|
public static final int new_passphrase = 0x21070001;
|
||||||
public static final int create = 0x21070002;
|
public static final int create = 0x21070002;
|
||||||
public static final int about = 0x21070003;
|
public static final int about = 0x21070003;
|
||||||
public static final int manage_public_keys = 0x21070004;
|
public static final int manage_public_keys = 0x21070004;
|
||||||
@ -85,12 +85,12 @@ public final class Id {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class dialog {
|
public static final class dialog {
|
||||||
public static final int pass_phrase = 0x21070001;
|
public static final int passphrase = 0x21070001;
|
||||||
public static final int encrypting = 0x21070002;
|
public static final int encrypting = 0x21070002;
|
||||||
public static final int decrypting = 0x21070003;
|
public static final int decrypting = 0x21070003;
|
||||||
public static final int new_pass_phrase = 0x21070004;
|
public static final int new_passphrase = 0x21070004;
|
||||||
public static final int pass_phrases_do_not_match = 0x21070005;
|
public static final int passphrases_do_not_match = 0x21070005;
|
||||||
public static final int no_pass_phrase = 0x21070006;
|
public static final int no_passphrase = 0x21070006;
|
||||||
public static final int saving = 0x21070007;
|
public static final int saving = 0x21070007;
|
||||||
public static final int delete_key = 0x21070008;
|
public static final int delete_key = 0x21070008;
|
||||||
public static final int import_keys = 0x21070009;
|
public static final int import_keys = 0x21070009;
|
||||||
|
@ -59,7 +59,6 @@ public class ClipboardReflection {
|
|||||||
* Wrapper around ClipboardManager based on Android version using Reflection API
|
* Wrapper around ClipboardManager based on Android version using Reflection API
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param text
|
|
||||||
*/
|
*/
|
||||||
public static CharSequence getClipboardText(Context context) {
|
public static CharSequence getClipboardText(Context context) {
|
||||||
Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
|
Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
@ -56,28 +56,33 @@ public class ActionBarHelper {
|
|||||||
* Sets custom view on ActionBar for Done/Cancel activities
|
* Sets custom view on ActionBar for Done/Cancel activities
|
||||||
*
|
*
|
||||||
* @param actionBar
|
* @param actionBar
|
||||||
* @param doneText
|
* @param firstText
|
||||||
* @param doneOnClickListener
|
* @param firstDrawableId
|
||||||
* @param cancelText
|
* @param firstOnClickListener
|
||||||
* @param cancelOnClickListener
|
* @param secondText
|
||||||
|
* @param secondDrawableId
|
||||||
|
* @param secondOnClickListener
|
||||||
*/
|
*/
|
||||||
public static void setDoneCancelView(ActionBar actionBar, int doneText,
|
public static void setTwoButtonView(ActionBar actionBar, int firstText, int firstDrawableId,
|
||||||
OnClickListener doneOnClickListener, int cancelText,
|
OnClickListener firstOnClickListener, int secondText, int secondDrawableId,
|
||||||
OnClickListener cancelOnClickListener) {
|
OnClickListener secondOnClickListener) {
|
||||||
|
|
||||||
// Inflate a "Done"/"Cancel" custom action bar view
|
// Inflate the custom action bar view
|
||||||
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
||||||
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
||||||
final View customActionBarView = inflater.inflate(
|
final View customActionBarView = inflater.inflate(
|
||||||
R.layout.actionbar_custom_view_done_cancel, null);
|
R.layout.actionbar_custom_view_done_cancel, null);
|
||||||
|
|
||||||
((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)).setText(doneText);
|
TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text));
|
||||||
|
firstTextView.setText(firstText);
|
||||||
|
firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0);
|
||||||
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
|
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
|
||||||
doneOnClickListener);
|
firstOnClickListener);
|
||||||
((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text))
|
TextView secondTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text));
|
||||||
.setText(cancelText);
|
secondTextView.setText(secondText);
|
||||||
|
secondTextView.setCompoundDrawablesWithIntrinsicBounds(secondDrawableId, 0, 0, 0);
|
||||||
customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
|
customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
|
||||||
cancelOnClickListener);
|
secondOnClickListener);
|
||||||
|
|
||||||
// Show the custom action bar view and hide the normal Home icon and title.
|
// Show the custom action bar view and hide the normal Home icon and title.
|
||||||
actionBar.setDisplayShowTitleEnabled(false);
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
@ -91,20 +96,22 @@ public class ActionBarHelper {
|
|||||||
* Sets custom view on ActionBar for Done activities
|
* Sets custom view on ActionBar for Done activities
|
||||||
*
|
*
|
||||||
* @param actionBar
|
* @param actionBar
|
||||||
* @param doneText
|
* @param firstText
|
||||||
* @param doneOnClickListener
|
* @param firstOnClickListener
|
||||||
*/
|
*/
|
||||||
public static void setDoneView(ActionBar actionBar, int doneText,
|
public static void setOneButtonView(ActionBar actionBar, int firstText, int firstDrawableId,
|
||||||
OnClickListener doneOnClickListener) {
|
OnClickListener firstOnClickListener) {
|
||||||
// Inflate a "Done" custom action bar view to serve as the "Up" affordance.
|
// Inflate a "Done" custom action bar view to serve as the "Up" affordance.
|
||||||
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
||||||
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
||||||
final View customActionBarView = inflater
|
final View customActionBarView = inflater
|
||||||
.inflate(R.layout.actionbar_custom_view_done, null);
|
.inflate(R.layout.actionbar_custom_view_done, null);
|
||||||
|
|
||||||
((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)).setText(doneText);
|
TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text));
|
||||||
|
firstTextView.setText(firstText);
|
||||||
|
firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0);
|
||||||
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
|
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
|
||||||
doneOnClickListener);
|
firstOnClickListener);
|
||||||
|
|
||||||
// Show the custom action bar view and hide the normal Home icon and title.
|
// Show the custom action bar view and hide the normal Home icon and title.
|
||||||
actionBar.setDisplayShowTitleEnabled(false);
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
@ -112,5 +119,4 @@ public class ActionBarHelper {
|
|||||||
actionBar.setDisplayShowCustomEnabled(true);
|
actionBar.setDisplayShowCustomEnabled(true);
|
||||||
actionBar.setCustomView(customActionBarView);
|
actionBar.setCustomView(customActionBarView);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.sufficientlysecure.keychain.helper;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Patterns;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ContactHelper {
|
||||||
|
|
||||||
|
public static final List<String> getMailAccounts(Context context) {
|
||||||
|
final Account[] accounts = AccountManager.get(context).getAccounts();
|
||||||
|
final Set<String> emailSet = new HashSet<String>();
|
||||||
|
for (Account account : accounts) {
|
||||||
|
if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
|
||||||
|
emailSet.add(account.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<String>(emailSet);
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,6 @@ import org.sufficientlysecure.keychain.Constants;
|
|||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||||
@ -63,7 +62,7 @@ public class ExportHelper {
|
|||||||
/**
|
/**
|
||||||
* Show dialog where to export keys
|
* Show dialog where to export keys
|
||||||
*/
|
*/
|
||||||
public void showExportKeysDialog(final Uri dataUri, final int keyType,
|
public void showExportKeysDialog(final long[] rowIds, final int keyType,
|
||||||
final String exportFilename) {
|
final String exportFilename) {
|
||||||
mExportFilename = exportFilename;
|
mExportFilename = exportFilename;
|
||||||
|
|
||||||
@ -75,7 +74,7 @@ public class ExportHelper {
|
|||||||
Bundle data = message.getData();
|
Bundle data = message.getData();
|
||||||
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
|
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
|
||||||
|
|
||||||
exportKeys(dataUri, keyType);
|
exportKeys(rowIds, keyType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -86,7 +85,7 @@ public class ExportHelper {
|
|||||||
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
|
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
String title = null;
|
String title = null;
|
||||||
if (dataUri == null) {
|
if (rowIds == null) {
|
||||||
// export all keys
|
// export all keys
|
||||||
title = activity.getString(R.string.title_export_keys);
|
title = activity.getString(R.string.title_export_keys);
|
||||||
} else {
|
} else {
|
||||||
@ -112,7 +111,7 @@ public class ExportHelper {
|
|||||||
/**
|
/**
|
||||||
* Export keys
|
* Export keys
|
||||||
*/
|
*/
|
||||||
public void exportKeys(Uri dataUri, int keyType) {
|
public void exportKeys(long[] rowIds, int keyType) {
|
||||||
Log.d(Constants.TAG, "exportKeys started");
|
Log.d(Constants.TAG, "exportKeys started");
|
||||||
|
|
||||||
// Send all information needed to service to export key in other thread
|
// Send all information needed to service to export key in other thread
|
||||||
@ -126,20 +125,17 @@ public class ExportHelper {
|
|||||||
data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
|
data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
|
||||||
data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
|
data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
|
||||||
|
|
||||||
if (dataUri == null) {
|
if (rowIds == null) {
|
||||||
data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
|
data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
|
||||||
} else {
|
} else {
|
||||||
// TODO: put data uri into service???
|
data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_ROW_ID, rowIds);
|
||||||
long keyRingMasterKeyId = ProviderHelper.getMasterKeyId(activity, dataUri);
|
|
||||||
|
|
||||||
data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after exporting is done in ApgService
|
// Message is received after exporting is done in ApgService
|
||||||
KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(activity,
|
KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(activity,
|
||||||
R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
|
activity.getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard ApgHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
@ -160,7 +156,7 @@ public class ExportHelper {
|
|||||||
Toast.makeText(activity, toastMessage, Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, toastMessage, Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.helper;
|
package org.sufficientlysecure.keychain.helper;
|
||||||
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -144,8 +144,8 @@ public class Preferences {
|
|||||||
Constants.defaults.KEY_SERVERS);
|
Constants.defaults.KEY_SERVERS);
|
||||||
Vector<String> servers = new Vector<String>();
|
Vector<String> servers = new Vector<String>();
|
||||||
String chunks[] = rawData.split(",");
|
String chunks[] = rawData.split(",");
|
||||||
for (int i = 0; i < chunks.length; ++i) {
|
for (String c : chunks) {
|
||||||
String tmp = chunks[i].trim();
|
String tmp = c.trim();
|
||||||
if (tmp.length() > 0) {
|
if (tmp.length() > 0) {
|
||||||
servers.add(tmp);
|
servers.add(tmp);
|
||||||
}
|
}
|
||||||
@ -156,8 +156,8 @@ public class Preferences {
|
|||||||
public void setKeyServers(String[] value) {
|
public void setKeyServers(String[] value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
String rawData = "";
|
String rawData = "";
|
||||||
for (int i = 0; i < value.length; ++i) {
|
for (String v : value) {
|
||||||
String tmp = value[i].trim();
|
String tmp = v.trim();
|
||||||
if (tmp.length() == 0) {
|
if (tmp.length() == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ public class PgpConversionHelper {
|
|||||||
*
|
*
|
||||||
* Singles keys are encoded as keyRings with one single key in it by Bouncy Castle
|
* Singles keys are encoded as keyRings with one single key in it by Bouncy Castle
|
||||||
*
|
*
|
||||||
* @param keysBytes
|
* @param keyBytes
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
|
public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
|
||||||
@ -149,7 +149,7 @@ public class PgpConversionHelper {
|
|||||||
/**
|
/**
|
||||||
* Convert from PGPSecretKey to byte[]
|
* Convert from PGPSecretKey to byte[]
|
||||||
*
|
*
|
||||||
* @param keysBytes
|
* @param key
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
|
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
|
||||||
@ -165,7 +165,7 @@ public class PgpConversionHelper {
|
|||||||
/**
|
/**
|
||||||
* Convert from PGPSecretKeyRing to byte[]
|
* Convert from PGPSecretKeyRing to byte[]
|
||||||
*
|
*
|
||||||
* @param keysBytes
|
* @param keyRing
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
|
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.spongycastle.bcpg.ArmoredInputStream;
|
import org.spongycastle.bcpg.ArmoredInputStream;
|
||||||
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
||||||
import org.spongycastle.openpgp.PGPCompressedData;
|
import org.spongycastle.openpgp.PGPCompressedData;
|
||||||
@ -36,6 +36,7 @@ import org.spongycastle.openpgp.PGPPublicKey;
|
|||||||
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
|
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPSignature;
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
import org.spongycastle.openpgp.PGPSignatureList;
|
import org.spongycastle.openpgp.PGPSignatureList;
|
||||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||||
@ -53,7 +54,7 @@ import org.sufficientlysecure.keychain.Constants;
|
|||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||||
@ -75,9 +76,10 @@ public class PgpDecryptVerify {
|
|||||||
private InputData data;
|
private InputData data;
|
||||||
private OutputStream outStream;
|
private OutputStream outStream;
|
||||||
|
|
||||||
private ProgressDialogUpdater progress;
|
private ProgressDialogUpdater progressDialogUpdater;
|
||||||
boolean assumeSymmetric;
|
private boolean assumeSymmetric;
|
||||||
String passphrase;
|
private String passphrase;
|
||||||
|
private long enforcedKeyId;
|
||||||
|
|
||||||
private PgpDecryptVerify(Builder builder) {
|
private PgpDecryptVerify(Builder builder) {
|
||||||
// private Constructor can only be called from Builder
|
// private Constructor can only be called from Builder
|
||||||
@ -85,9 +87,10 @@ public class PgpDecryptVerify {
|
|||||||
this.data = builder.data;
|
this.data = builder.data;
|
||||||
this.outStream = builder.outStream;
|
this.outStream = builder.outStream;
|
||||||
|
|
||||||
this.progress = builder.progress;
|
this.progressDialogUpdater = builder.progressDialogUpdater;
|
||||||
this.assumeSymmetric = builder.assumeSymmetric;
|
this.assumeSymmetric = builder.assumeSymmetric;
|
||||||
this.passphrase = builder.passphrase;
|
this.passphrase = builder.passphrase;
|
||||||
|
this.enforcedKeyId = builder.enforcedKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
@ -97,9 +100,10 @@ public class PgpDecryptVerify {
|
|||||||
private OutputStream outStream;
|
private OutputStream outStream;
|
||||||
|
|
||||||
// optional
|
// optional
|
||||||
private ProgressDialogUpdater progress = null;
|
private ProgressDialogUpdater progressDialogUpdater = null;
|
||||||
private boolean assumeSymmetric = false;
|
private boolean assumeSymmetric = false;
|
||||||
private String passphrase = "";
|
private String passphrase = "";
|
||||||
|
private long enforcedKeyId = 0;
|
||||||
|
|
||||||
public Builder(Context context, InputData data, OutputStream outStream) {
|
public Builder(Context context, InputData data, OutputStream outStream) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@ -107,8 +111,8 @@ public class PgpDecryptVerify {
|
|||||||
this.outStream = outStream;
|
this.outStream = outStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder progress(ProgressDialogUpdater progress) {
|
public Builder progressDialogUpdater(ProgressDialogUpdater progressDialogUpdater) {
|
||||||
this.progress = progress;
|
this.progressDialogUpdater = progressDialogUpdater;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,20 +126,32 @@ public class PgpDecryptVerify {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow this key id alone for decryption.
|
||||||
|
* This means only ciphertexts encrypted for this private key can be decrypted.
|
||||||
|
*
|
||||||
|
* @param enforcedKeyId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Builder enforcedKeyId(long enforcedKeyId) {
|
||||||
|
this.enforcedKeyId = enforcedKeyId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public PgpDecryptVerify build() {
|
public PgpDecryptVerify build() {
|
||||||
return new PgpDecryptVerify(this);
|
return new PgpDecryptVerify(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateProgress(int message, int current, int total) {
|
public void updateProgress(int message, int current, int total) {
|
||||||
if (progress != null) {
|
if (progressDialogUpdater != null) {
|
||||||
progress.setProgress(message, current, total);
|
progressDialogUpdater.setProgress(message, current, total);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateProgress(int current, int total) {
|
public void updateProgress(int current, int total) {
|
||||||
if (progress != null) {
|
if (progressDialogUpdater != null) {
|
||||||
progress.setProgress(current, total);
|
progressDialogUpdater.setProgress(current, total);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,9 +193,8 @@ public class PgpDecryptVerify {
|
|||||||
* @throws PGPException
|
* @throws PGPException
|
||||||
* @throws SignatureException
|
* @throws SignatureException
|
||||||
*/
|
*/
|
||||||
public Bundle execute()
|
public PgpDecryptVerifyResult execute()
|
||||||
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||||
|
|
||||||
// automatically works with ascii armor input and binary
|
// automatically works with ascii armor input and binary
|
||||||
InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
|
InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
|
||||||
if (in instanceof ArmoredInputStream) {
|
if (in instanceof ArmoredInputStream) {
|
||||||
@ -207,9 +222,9 @@ public class PgpDecryptVerify {
|
|||||||
* @throws PGPException
|
* @throws PGPException
|
||||||
* @throws SignatureException
|
* @throws SignatureException
|
||||||
*/
|
*/
|
||||||
private Bundle decryptVerify(InputStream in)
|
private PgpDecryptVerifyResult decryptVerify(InputStream in)
|
||||||
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||||
Bundle returnData = new Bundle();
|
PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult();
|
||||||
|
|
||||||
PGPObjectFactory pgpF = new PGPObjectFactory(in);
|
PGPObjectFactory pgpF = new PGPObjectFactory(in);
|
||||||
PGPEncryptedDataList enc;
|
PGPEncryptedDataList enc;
|
||||||
@ -277,9 +292,40 @@ public class PgpDecryptVerify {
|
|||||||
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
|
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
|
||||||
secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID());
|
secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID());
|
||||||
if (secretKey != null) {
|
if (secretKey != null) {
|
||||||
|
// secret key exists in database
|
||||||
|
|
||||||
|
// allow only a specific key for decryption?
|
||||||
|
if (enforcedKeyId != 0) {
|
||||||
|
// TODO: improve this code! get master key directly!
|
||||||
|
PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, encData.getKeyID());
|
||||||
|
long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID();
|
||||||
|
Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
|
||||||
|
Log.d(Constants.TAG, "enforcedKeyId: " + enforcedKeyId);
|
||||||
|
Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
|
||||||
|
|
||||||
|
if (enforcedKeyId != masterKeyId) {
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pbe = encData;
|
pbe = encData;
|
||||||
|
|
||||||
|
// if no passphrase was explicitly set try to get it from the cache service
|
||||||
|
if (passphrase == null) {
|
||||||
|
// returns "" if key has no passphrase
|
||||||
|
passphrase = PassphraseCacheService.getCachedPassphrase(context, encData.getKeyID());
|
||||||
|
|
||||||
|
// if passphrase was not cached, return here indicating that a passphrase is missing!
|
||||||
|
if (passphrase == null) {
|
||||||
|
returnData.setKeyPassphraseNeeded(true);
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +335,7 @@ public class PgpDecryptVerify {
|
|||||||
|
|
||||||
currentProgress += 5;
|
currentProgress += 5;
|
||||||
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
|
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
|
||||||
PGPPrivateKey privateKey = null;
|
PGPPrivateKey privateKey;
|
||||||
try {
|
try {
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
@ -317,6 +363,7 @@ public class PgpDecryptVerify {
|
|||||||
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
|
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
|
||||||
Object dataChunk = plainFact.nextObject();
|
Object dataChunk = plainFact.nextObject();
|
||||||
PGPOnePassSignature signature = null;
|
PGPOnePassSignature signature = null;
|
||||||
|
OpenPgpSignatureResult signatureResult = null;
|
||||||
PGPPublicKey signatureKey = null;
|
PGPPublicKey signatureKey = null;
|
||||||
int signatureIndex = -1;
|
int signatureIndex = -1;
|
||||||
|
|
||||||
@ -334,7 +381,7 @@ public class PgpDecryptVerify {
|
|||||||
if (dataChunk instanceof PGPOnePassSignatureList) {
|
if (dataChunk instanceof PGPOnePassSignatureList) {
|
||||||
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
|
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
|
||||||
|
|
||||||
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
|
signatureResult = new OpenPgpSignatureResult();
|
||||||
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
|
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
|
||||||
for (int i = 0; i < sigList.size(); ++i) {
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
signature = sigList.get(i);
|
signature = sigList.get(i);
|
||||||
@ -354,12 +401,12 @@ public class PgpDecryptVerify {
|
|||||||
if (signKeyRing != null) {
|
if (signKeyRing != null) {
|
||||||
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
||||||
}
|
}
|
||||||
returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
|
signatureResult.setUserId(userId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
|
signatureResult.setKeyId(signatureKeyId);
|
||||||
|
|
||||||
if (signature != null) {
|
if (signature != null) {
|
||||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
||||||
@ -367,7 +414,7 @@ public class PgpDecryptVerify {
|
|||||||
|
|
||||||
signature.init(contentVerifierBuilderProvider, signatureKey);
|
signature.init(contentVerifierBuilderProvider, signatureKey);
|
||||||
} else {
|
} else {
|
||||||
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
|
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataChunk = plainFact.nextObject();
|
dataChunk = plainFact.nextObject();
|
||||||
@ -405,8 +452,7 @@ public class PgpDecryptVerify {
|
|||||||
try {
|
try {
|
||||||
signature.update(buffer, 0, n);
|
signature.update(buffer, 0, n);
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
returnData
|
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
|
||||||
.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
|
|
||||||
signature = null;
|
signature = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -430,17 +476,20 @@ public class PgpDecryptVerify {
|
|||||||
PGPSignature messageSignature = signatureList.get(signatureIndex);
|
PGPSignature messageSignature = signatureList.get(signatureIndex);
|
||||||
|
|
||||||
// these are not cleartext signatures!
|
// these are not cleartext signatures!
|
||||||
returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false);
|
// TODO: what about binary signatures?
|
||||||
|
signatureResult.setSignatureOnly(false);
|
||||||
|
|
||||||
//Now check binding signatures
|
//Now check binding signatures
|
||||||
boolean keyBinding_isok = verifyKeyBinding(context, messageSignature, signatureKey);
|
boolean validKeyBinding = verifyKeyBinding(context, messageSignature, signatureKey);
|
||||||
boolean sig_isok = signature.verify(messageSignature);
|
boolean validSignature = signature.verify(messageSignature);
|
||||||
|
|
||||||
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, keyBinding_isok & sig_isok);
|
// TODO: implement CERTIFIED!
|
||||||
|
if (validKeyBinding & validSignature) {
|
||||||
|
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: test if this integrity really check works!
|
|
||||||
if (encryptedData.isIntegrityProtected()) {
|
if (encryptedData.isIntegrityProtected()) {
|
||||||
updateProgress(R.string.progress_verifying_integrity, 95, 100);
|
updateProgress(R.string.progress_verifying_integrity, 95, 100);
|
||||||
|
|
||||||
@ -455,9 +504,12 @@ public class PgpDecryptVerify {
|
|||||||
} else {
|
} else {
|
||||||
// no integrity check
|
// no integrity check
|
||||||
Log.e(Constants.TAG, "Encrypted data was not integrity protected!");
|
Log.e(Constants.TAG, "Encrypted data was not integrity protected!");
|
||||||
|
// TODO: inform user?
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
|
||||||
|
returnData.setSignatureResult(signatureResult);
|
||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,11 +526,12 @@ public class PgpDecryptVerify {
|
|||||||
* @throws PGPException
|
* @throws PGPException
|
||||||
* @throws SignatureException
|
* @throws SignatureException
|
||||||
*/
|
*/
|
||||||
private Bundle verifyCleartextSignature(ArmoredInputStream aIn)
|
private PgpDecryptVerifyResult verifyCleartextSignature(ArmoredInputStream aIn)
|
||||||
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||||
Bundle returnData = new Bundle();
|
PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult();
|
||||||
|
OpenPgpSignatureResult signatureResult = new OpenPgpSignatureResult();
|
||||||
// cleartext signatures are never encrypted ;)
|
// cleartext signatures are never encrypted ;)
|
||||||
returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, true);
|
signatureResult.setSignatureOnly(true);
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
|
||||||
@ -504,8 +557,6 @@ public class PgpDecryptVerify {
|
|||||||
byte[] clearText = out.toByteArray();
|
byte[] clearText = out.toByteArray();
|
||||||
outStream.write(clearText);
|
outStream.write(clearText);
|
||||||
|
|
||||||
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
|
|
||||||
|
|
||||||
updateProgress(R.string.progress_processing_signature, 60, 100);
|
updateProgress(R.string.progress_processing_signature, 60, 100);
|
||||||
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
|
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
|
||||||
|
|
||||||
@ -533,15 +584,17 @@ public class PgpDecryptVerify {
|
|||||||
if (signKeyRing != null) {
|
if (signKeyRing != null) {
|
||||||
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
||||||
}
|
}
|
||||||
returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
|
signatureResult.setUserId(userId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
|
signatureResult.setKeyId(signatureKeyId);
|
||||||
|
|
||||||
if (signature == null) {
|
if (signature == null) {
|
||||||
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
|
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY);
|
||||||
|
returnData.setSignatureResult(signatureResult);
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
@ -569,12 +622,17 @@ public class PgpDecryptVerify {
|
|||||||
} while (lookAhead != -1);
|
} while (lookAhead != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean sig_isok = signature.verify();
|
|
||||||
|
|
||||||
//Now check binding signatures
|
//Now check binding signatures
|
||||||
boolean keyBinding_isok = verifyKeyBinding(context, signature, signatureKey);
|
boolean validKeyBinding = verifyKeyBinding(context, signature, signatureKey);
|
||||||
|
boolean validSignature = signature.verify();
|
||||||
|
|
||||||
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok);
|
if (validSignature & validKeyBinding) {
|
||||||
|
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: what about SIGNATURE_SUCCESS_CERTIFIED and SIGNATURE_ERROR????
|
||||||
|
|
||||||
|
returnData.setSignatureResult(signatureResult);
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
return returnData;
|
return returnData;
|
||||||
@ -582,34 +640,34 @@ public class PgpDecryptVerify {
|
|||||||
|
|
||||||
private static boolean verifyKeyBinding(Context context, PGPSignature signature, PGPPublicKey signatureKey) {
|
private static boolean verifyKeyBinding(Context context, PGPSignature signature, PGPPublicKey signatureKey) {
|
||||||
long signatureKeyId = signature.getKeyID();
|
long signatureKeyId = signature.getKeyID();
|
||||||
boolean keyBinding_isok = false;
|
boolean validKeyBinding = false;
|
||||||
String userId = null;
|
|
||||||
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
|
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
|
||||||
signatureKeyId);
|
signatureKeyId);
|
||||||
PGPPublicKey mKey = null;
|
PGPPublicKey mKey = null;
|
||||||
if (signKeyRing != null) {
|
if (signKeyRing != null) {
|
||||||
mKey = PgpKeyHelper.getMasterKey(signKeyRing);
|
mKey = PgpKeyHelper.getMasterKey(signKeyRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signature.getKeyID() != mKey.getKeyID()) {
|
if (signature.getKeyID() != mKey.getKeyID()) {
|
||||||
keyBinding_isok = verifyKeyBinding(mKey, signatureKey);
|
validKeyBinding = verifyKeyBinding(mKey, signatureKey);
|
||||||
} else { //if the key used to make the signature was the master key, no need to check binding sigs
|
} else { //if the key used to make the signature was the master key, no need to check binding sigs
|
||||||
keyBinding_isok = true;
|
validKeyBinding = true;
|
||||||
}
|
}
|
||||||
return keyBinding_isok;
|
return validKeyBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
private static boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
||||||
boolean subkeyBinding_isok = false;
|
boolean validSubkeyBinding = false;
|
||||||
boolean tmp_subkeyBinding_isok = false;
|
boolean validTempSubkeyBinding = false;
|
||||||
boolean primkeyBinding_isok = false;
|
boolean validPrimaryKeyBinding = false;
|
||||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||||
|
new JcaPGPContentVerifierBuilderProvider()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
|
Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
|
||||||
|
|
||||||
subkeyBinding_isok = false;
|
|
||||||
tmp_subkeyBinding_isok = false;
|
|
||||||
primkeyBinding_isok = false;
|
|
||||||
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
|
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
|
||||||
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
|
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
|
||||||
//about keys without subkey signing. Can't get it to import a slightly broken one
|
//about keys without subkey signing. Can't get it to import a slightly broken one
|
||||||
@ -619,32 +677,36 @@ public class PgpDecryptVerify {
|
|||||||
//check and if ok, check primary key binding.
|
//check and if ok, check primary key binding.
|
||||||
try {
|
try {
|
||||||
sig.init(contentVerifierBuilderProvider, masterPublicKey);
|
sig.init(contentVerifierBuilderProvider, masterPublicKey);
|
||||||
tmp_subkeyBinding_isok = sig.verifyCertification(masterPublicKey, signingPublicKey);
|
validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey);
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
continue;
|
continue;
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp_subkeyBinding_isok)
|
if (validTempSubkeyBinding)
|
||||||
subkeyBinding_isok = true;
|
validSubkeyBinding = true;
|
||||||
if (tmp_subkeyBinding_isok) {
|
if (validTempSubkeyBinding) {
|
||||||
primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), masterPublicKey, signingPublicKey);
|
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
|
||||||
if (primkeyBinding_isok)
|
masterPublicKey, signingPublicKey);
|
||||||
|
if (validPrimaryKeyBinding)
|
||||||
break;
|
break;
|
||||||
primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), masterPublicKey, signingPublicKey);
|
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
|
||||||
if (primkeyBinding_isok)
|
masterPublicKey, signingPublicKey);
|
||||||
|
if (validPrimaryKeyBinding)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (subkeyBinding_isok & primkeyBinding_isok);
|
return (validSubkeyBinding & validPrimaryKeyBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean verifyPrimaryBinding(PGPSignatureSubpacketVector Pkts, PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector Pkts,
|
||||||
boolean primkeyBinding_isok = false;
|
PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
||||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
boolean validPrimaryKeyBinding = false;
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||||
|
new JcaPGPContentVerifierBuilderProvider()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
PGPSignatureList eSigList;
|
PGPSignatureList eSigList;
|
||||||
|
|
||||||
if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
|
if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
|
||||||
@ -660,8 +722,8 @@ public class PgpDecryptVerify {
|
|||||||
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
|
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
|
||||||
try {
|
try {
|
||||||
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
|
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
|
||||||
primkeyBinding_isok = emSig.verifyCertification(masterPublicKey, signingPublicKey);
|
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
|
||||||
if (primkeyBinding_isok)
|
if (validPrimaryKeyBinding)
|
||||||
break;
|
break;
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
continue;
|
continue;
|
||||||
@ -671,7 +733,8 @@ public class PgpDecryptVerify {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return primkeyBinding_isok;
|
|
||||||
|
return validPrimaryKeyBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -680,10 +743,9 @@ public class PgpDecryptVerify {
|
|||||||
* @param sig
|
* @param sig
|
||||||
* @param line
|
* @param line
|
||||||
* @throws SignatureException
|
* @throws SignatureException
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
private static void processLine(PGPSignature sig, byte[] line)
|
private static void processLine(PGPSignature sig, byte[] line)
|
||||||
throws SignatureException, IOException {
|
throws SignatureException {
|
||||||
int length = getLengthWithoutWhiteSpace(line);
|
int length = getLengthWithoutWhiteSpace(line);
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
sig.update(line, 0, length);
|
sig.update(line, 0, length);
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
|
|
||||||
|
public class PgpDecryptVerifyResult implements Parcelable {
|
||||||
|
boolean symmetricPassphraseNeeded;
|
||||||
|
boolean keyPassphraseNeeded;
|
||||||
|
OpenPgpSignatureResult signatureResult;
|
||||||
|
|
||||||
|
public boolean isSymmetricPassphraseNeeded() {
|
||||||
|
return symmetricPassphraseNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSymmetricPassphraseNeeded(boolean symmetricPassphraseNeeded) {
|
||||||
|
this.symmetricPassphraseNeeded = symmetricPassphraseNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isKeyPassphraseNeeded() {
|
||||||
|
return keyPassphraseNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyPassphraseNeeded(boolean keyPassphraseNeeded) {
|
||||||
|
this.keyPassphraseNeeded = keyPassphraseNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenPgpSignatureResult getSignatureResult() {
|
||||||
|
return signatureResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
|
||||||
|
this.signatureResult = signatureResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PgpDecryptVerifyResult() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) {
|
||||||
|
this.symmetricPassphraseNeeded = b.symmetricPassphraseNeeded;
|
||||||
|
this.keyPassphraseNeeded = b.keyPassphraseNeeded;
|
||||||
|
this.signatureResult = b.signatureResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeByte((byte) (symmetricPassphraseNeeded ? 1 : 0));
|
||||||
|
dest.writeByte((byte) (keyPassphraseNeeded ? 1 : 0));
|
||||||
|
dest.writeParcelable(signatureResult, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() {
|
||||||
|
public PgpDecryptVerifyResult createFromParcel(final Parcel source) {
|
||||||
|
PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult();
|
||||||
|
vr.symmetricPassphraseNeeded = source.readByte() == 1;
|
||||||
|
vr.keyPassphraseNeeded = source.readByte() == 1;
|
||||||
|
vr.signatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
|
||||||
|
return vr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PgpDecryptVerifyResult[] newArray(final int size) {
|
||||||
|
return new PgpDecryptVerifyResult[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -193,11 +193,10 @@ public class PgpHelper {
|
|||||||
* @param context
|
* @param context
|
||||||
* @param progress
|
* @param progress
|
||||||
* @param file
|
* @param file
|
||||||
* @throws FileNotFoundException
|
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file)
|
public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file)
|
||||||
throws FileNotFoundException, IOException {
|
throws IOException {
|
||||||
long length = file.length();
|
long length = file.length();
|
||||||
SecureRandom random = new SecureRandom();
|
SecureRandom random = new SecureRandom();
|
||||||
RandomAccessFile raf = new RandomAccessFile(file, "rws");
|
RandomAccessFile raf = new RandomAccessFile(file, "rws");
|
||||||
|
@ -17,11 +17,9 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -29,12 +27,10 @@ import java.util.List;
|
|||||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||||
import org.spongycastle.openpgp.PGPException;
|
import org.spongycastle.openpgp.PGPException;
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
import org.spongycastle.openpgp.PGPKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKey;
|
import org.spongycastle.openpgp.PGPPublicKey;
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
@ -44,11 +40,9 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||||
import org.sufficientlysecure.keychain.util.HkpKeyServer;
|
import org.sufficientlysecure.keychain.util.HkpKeyServer;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
|
||||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
|
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
|
|
||||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -85,13 +79,14 @@ public class PgpImportExport {
|
|||||||
|
|
||||||
public boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
|
public boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
|
ArmoredOutputStream aos = null;
|
||||||
try {
|
try {
|
||||||
|
aos = new ArmoredOutputStream(bos);
|
||||||
aos.write(keyring.getEncoded());
|
aos.write(keyring.getEncoded());
|
||||||
aos.close();
|
aos.close();
|
||||||
|
|
||||||
String armouredKey = bos.toString("UTF-8");
|
String armoredKey = bos.toString("UTF-8");
|
||||||
server.add(armouredKey);
|
server.add(armoredKey);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -101,7 +96,8 @@ public class PgpImportExport {
|
|||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
bos.close();
|
if (aos != null) aos.close();
|
||||||
|
if (bos != null) bos.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,59 +157,53 @@ public class PgpImportExport {
|
|||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bundle exportKeyRings(ArrayList<Long> keyRingMasterKeyIds, int keyType,
|
public Bundle exportKeyRings(ArrayList<Long> keyRingRowIds, int keyType,
|
||||||
OutputStream outStream) throws PgpGeneralException, FileNotFoundException,
|
OutputStream outStream) throws PgpGeneralException,
|
||||||
PGPException, IOException {
|
PGPException, IOException {
|
||||||
Bundle returnData = new Bundle();
|
Bundle returnData = new Bundle();
|
||||||
|
|
||||||
|
int rowIdsSize = keyRingRowIds.size();
|
||||||
|
|
||||||
updateProgress(
|
updateProgress(
|
||||||
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
|
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
|
||||||
keyRingMasterKeyIds.size()), 0, 100);
|
rowIdsSize), 0, 100);
|
||||||
|
|
||||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||||
throw new PgpGeneralException(
|
throw new PgpGeneralException(
|
||||||
mContext.getString(R.string.error_external_storage_not_ready));
|
mContext.getString(R.string.error_external_storage_not_ready));
|
||||||
}
|
}
|
||||||
|
// For each row id
|
||||||
|
for (int i = 0; i < rowIdsSize; ++i) {
|
||||||
|
// Create an output stream
|
||||||
|
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
|
||||||
|
arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
||||||
|
|
||||||
if (keyType == Id.type.secret_key) {
|
// If the keyType is secret get the PGPSecretKeyRing
|
||||||
ArmoredOutputStream outSec = new ArmoredOutputStream(outStream);
|
// based on the row id and encode it to the output
|
||||||
outSec.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
if (keyType == Id.type.secret_key) {
|
||||||
|
updateProgress(i * 100 / rowIdsSize / 2, 100);
|
||||||
for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
|
PGPSecretKeyRing secretKeyRing =
|
||||||
updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
|
ProviderHelper.getPGPSecretKeyRingByRowId(mContext, keyRingRowIds.get(i));
|
||||||
|
|
||||||
PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
|
|
||||||
mContext, keyRingMasterKeyIds.get(i));
|
|
||||||
|
|
||||||
if (secretKeyRing != null) {
|
if (secretKeyRing != null) {
|
||||||
secretKeyRing.encode(outSec);
|
secretKeyRing.encode(arOutStream);
|
||||||
}
|
}
|
||||||
}
|
// Else if it's a public key get the PGPPublicKeyRing
|
||||||
outSec.close();
|
// and encode that to the output
|
||||||
} else {
|
} else {
|
||||||
// export public keyrings...
|
updateProgress(i * 100 / rowIdsSize, 100);
|
||||||
ArmoredOutputStream outPub = new ArmoredOutputStream(outStream);
|
PGPPublicKeyRing publicKeyRing =
|
||||||
outPub.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
ProviderHelper.getPGPPublicKeyRingByRowId(mContext, keyRingRowIds.get(i));
|
||||||
|
|
||||||
for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
|
|
||||||
// double the needed time if exporting both public and secret parts
|
|
||||||
if (keyType == Id.type.secret_key) {
|
|
||||||
updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
|
|
||||||
} else {
|
|
||||||
updateProgress(i * 100 / keyRingMasterKeyIds.size(), 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(
|
|
||||||
mContext, keyRingMasterKeyIds.get(i));
|
|
||||||
|
|
||||||
if (publicKeyRing != null) {
|
if (publicKeyRing != null) {
|
||||||
publicKeyRing.encode(outPub);
|
publicKeyRing.encode(arOutStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
outPub.close();
|
|
||||||
|
arOutStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
returnData.putInt(KeychainIntentService.RESULT_EXPORT, keyRingMasterKeyIds.size());
|
returnData.putInt(KeychainIntentService.RESULT_EXPORT, rowIdsSize);
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
|
||||||
@ -234,7 +224,7 @@ public class PgpImportExport {
|
|||||||
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
|
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
|
||||||
secretKeyRing.getSecretKeys())) {
|
secretKeyRing.getSecretKeys())) {
|
||||||
if (!testSecretKey.isMasterKey()) {
|
if (!testSecretKey.isMasterKey()) {
|
||||||
if (PgpKeyHelper.isSecretKeyPrivateEmpty(testSecretKey)) {
|
if (testSecretKey.isPrivateKeyEmpty()) {
|
||||||
// this is bad, something is very wrong...
|
// this is bad, something is very wrong...
|
||||||
save = false;
|
save = false;
|
||||||
status = Id.return_value.bad;
|
status = Id.return_value.bad;
|
||||||
|
@ -32,6 +32,7 @@ import org.spongycastle.openpgp.PGPSecretKey;
|
|||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPSignature;
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||||
|
import org.spongycastle.util.encoders.Hex;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
@ -415,28 +416,27 @@ public class PgpKeyHelper {
|
|||||||
String algorithmStr = null;
|
String algorithmStr = null;
|
||||||
|
|
||||||
switch (algorithm) {
|
switch (algorithm) {
|
||||||
case PGPPublicKey.RSA_ENCRYPT:
|
case PGPPublicKey.RSA_ENCRYPT:
|
||||||
case PGPPublicKey.RSA_GENERAL:
|
case PGPPublicKey.RSA_GENERAL:
|
||||||
case PGPPublicKey.RSA_SIGN: {
|
case PGPPublicKey.RSA_SIGN: {
|
||||||
algorithmStr = "RSA";
|
algorithmStr = "RSA";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PGPPublicKey.DSA: {
|
||||||
|
algorithmStr = "DSA";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case PGPPublicKey.DSA: {
|
case PGPPublicKey.ELGAMAL_ENCRYPT:
|
||||||
algorithmStr = "DSA";
|
case PGPPublicKey.ELGAMAL_GENERAL: {
|
||||||
break;
|
algorithmStr = "ElGamal";
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case PGPPublicKey.ELGAMAL_ENCRYPT:
|
default: {
|
||||||
case PGPPublicKey.ELGAMAL_GENERAL: {
|
algorithmStr = "Unknown";
|
||||||
algorithmStr = "ElGamal";
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
algorithmStr = "Unknown";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(keySize > 0)
|
if(keySize > 0)
|
||||||
return algorithmStr + ", " + keySize + " bit";
|
return algorithmStr + ", " + keySize + " bit";
|
||||||
@ -444,31 +444,6 @@ public class PgpKeyHelper {
|
|||||||
return algorithmStr;
|
return algorithmStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts fingerprint to hex with whitespaces after 4 characters
|
|
||||||
*
|
|
||||||
* @param fp
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String convertFingerprintToHex(byte[] fp, boolean chunked) {
|
|
||||||
String fingerPrint = "";
|
|
||||||
for (int i = 0; i < fp.length; ++i) {
|
|
||||||
if (chunked && i != 0 && i % 10 == 0) {
|
|
||||||
fingerPrint += " ";
|
|
||||||
} else if (chunked && i != 0 && i % 2 == 0) {
|
|
||||||
fingerPrint += " ";
|
|
||||||
}
|
|
||||||
String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(Locale.US);
|
|
||||||
while (chunk.length() < 2) {
|
|
||||||
chunk = "0" + chunk;
|
|
||||||
}
|
|
||||||
fingerPrint += chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fingerPrint;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFingerPrint(Context context, long keyId) {
|
public static String getFingerPrint(Context context, long keyId) {
|
||||||
PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId);
|
PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId);
|
||||||
// if it is no public key get it from your own keys...
|
// if it is no public key get it from your own keys...
|
||||||
@ -484,52 +459,68 @@ public class PgpKeyHelper {
|
|||||||
return convertFingerprintToHex(key.getFingerprint(), true);
|
return convertFingerprintToHex(key.getFingerprint(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) {
|
/**
|
||||||
return secretKey.isPrivateKeyEmpty();
|
* Converts fingerprint to hex (optional: with whitespaces after 4 characters)
|
||||||
}
|
* <p/>
|
||||||
|
* Fingerprint is shown using lowercase characters. Studies have shown that humans can
|
||||||
// public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) {
|
* better differentiate between numbers and letters when letters are lowercase.
|
||||||
// PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
|
*
|
||||||
// if (secretKey == null) {
|
* @param fingerprint
|
||||||
// Log.e(Constants.TAG, "Key could not be found!");
|
* @param split split into 4 character chunks
|
||||||
// return false; // could be a public key, assume it is not empty
|
* @return
|
||||||
// }
|
*/
|
||||||
// return isSecretKeyPrivateEmpty(secretKey);
|
public static String convertFingerprintToHex(byte[] fingerprint, boolean split) {
|
||||||
// }
|
String hexString = Hex.toHexString(fingerprint);
|
||||||
|
if (split) {
|
||||||
public static String convertKeyIdToHex(long keyId) {
|
hexString = hexString.replaceAll("(.{4})(?!$)", "$1 ");
|
||||||
String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US);
|
|
||||||
while (fingerPrint.length() < 8) {
|
|
||||||
fingerPrint = "0" + fingerPrint;
|
|
||||||
}
|
}
|
||||||
return fingerPrint;
|
|
||||||
|
return hexString;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: documentation
|
* Convert key id from long to 64 bit hex string
|
||||||
*
|
* <p/>
|
||||||
|
* V4: "The Key ID is the low-order 64 bits of the fingerprint"
|
||||||
|
* <p/>
|
||||||
|
* see http://tools.ietf.org/html/rfc4880#section-12.2
|
||||||
|
*
|
||||||
* @param keyId
|
* @param keyId
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String convertKeyToHex(long keyId) {
|
public static String convertKeyIdToHex(long keyId) {
|
||||||
return convertKeyIdToHex(keyId >> 32) + convertKeyIdToHex(keyId);
|
return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long convertHexToKeyId(String data) {
|
private static String convertKeyIdToHex32bit(long keyId) {
|
||||||
int len = data.length();
|
String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.US);
|
||||||
String s2 = data.substring(len - 8);
|
while (hexString.length() < 8) {
|
||||||
String s1 = data.substring(0, len - 8);
|
hexString = "0" + hexString;
|
||||||
|
}
|
||||||
|
return hexString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used in HkpKeyServer to convert hex encoded key ids back to long.
|
||||||
|
*
|
||||||
|
* @param hexString
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static long convertHexToKeyId(String hexString) {
|
||||||
|
int len = hexString.length();
|
||||||
|
String s2 = hexString.substring(len - 8);
|
||||||
|
String s1 = hexString.substring(0, len - 8);
|
||||||
return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
|
return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits userId string into naming part, email part, and comment part
|
* Splits userId string into naming part, email part, and comment part
|
||||||
*
|
*
|
||||||
* @param userId
|
* @param userId
|
||||||
* @return array with naming (0), email (1), comment (2)
|
* @return array with naming (0), email (1), comment (2)
|
||||||
*/
|
*/
|
||||||
public static String[] splitUserId(String userId) {
|
public static String[] splitUserId(String userId) {
|
||||||
String[] result = new String[] { null, null, null };
|
String[] result = new String[]{null, null, null};
|
||||||
|
|
||||||
if (userId == null || userId.equals("")) {
|
if (userId == null || userId.equals("")) {
|
||||||
return result;
|
return result;
|
||||||
@ -550,7 +541,6 @@ public class PgpKeyHelper {
|
|||||||
result[0] = matcher.group(1);
|
result[0] = matcher.group(1);
|
||||||
result[1] = matcher.group(3);
|
result[1] = matcher.group(3);
|
||||||
result[2] = matcher.group(2);
|
result[2] = matcher.group(2);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -107,7 +107,7 @@ public class PgpKeyOperation {
|
|||||||
*
|
*
|
||||||
* @param algorithmChoice
|
* @param algorithmChoice
|
||||||
* @param keySize
|
* @param keySize
|
||||||
* @param passPhrase
|
* @param passphrase
|
||||||
* @param isMasterKey
|
* @param isMasterKey
|
||||||
* @return
|
* @return
|
||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
@ -118,7 +118,7 @@ public class PgpKeyOperation {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO: key flags?
|
// TODO: key flags?
|
||||||
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passPhrase,
|
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
|
||||||
boolean isMasterKey) throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
|
boolean isMasterKey) throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
|
||||||
PgpGeneralException, InvalidAlgorithmParameterException {
|
PgpGeneralException, InvalidAlgorithmParameterException {
|
||||||
|
|
||||||
@ -126,8 +126,8 @@ public class PgpKeyOperation {
|
|||||||
throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
|
throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passPhrase == null) {
|
if (passphrase == null) {
|
||||||
passPhrase = "";
|
passphrase = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
int algorithm = 0;
|
int algorithm = 0;
|
||||||
@ -181,7 +181,7 @@ public class PgpKeyOperation {
|
|||||||
// Build key encrypter and decrypter based on passphrase
|
// Build key encrypter and decrypter based on passphrase
|
||||||
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
|
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
|
||||||
PGPEncryptedData.CAST5, sha1Calc)
|
PGPEncryptedData.CAST5, sha1Calc)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passPhrase.toCharArray());
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||||
|
|
||||||
PGPSecretKey secKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
|
PGPSecretKey secKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
|
||||||
sha1Calc, isMasterKey, keyEncryptor);
|
sha1Calc, isMasterKey, keyEncryptor);
|
||||||
@ -190,7 +190,7 @@ public class PgpKeyOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
|
public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
|
||||||
String newPassPhrase) throws IOException, PGPException, PGPException,
|
String newPassPhrase) throws IOException, PGPException,
|
||||||
NoSuchProviderException {
|
NoSuchProviderException {
|
||||||
|
|
||||||
updateProgress(R.string.progress_building_key, 0, 100);
|
updateProgress(R.string.progress_building_key, 0, 100);
|
||||||
|
@ -342,7 +342,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
/**
|
/**
|
||||||
* Returns type of the query (secret/public)
|
* Returns type of the query (secret/public)
|
||||||
*
|
*
|
||||||
* @param uri
|
* @param match
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private int getKeyType(int match) {
|
private int getKeyType(int match) {
|
||||||
@ -1039,7 +1039,8 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
* Build default selection statement for KeyRings. If no extra selection is specified only build
|
* Build default selection statement for KeyRings. If no extra selection is specified only build
|
||||||
* where clause with rowId
|
* where clause with rowId
|
||||||
*
|
*
|
||||||
* @param uri
|
* @param defaultSelection
|
||||||
|
* @param keyType
|
||||||
* @param selection
|
* @param selection
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@ -100,7 +100,7 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId
|
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
|
||||||
*/
|
*/
|
||||||
public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context,
|
public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context,
|
||||||
long masterKeyId) {
|
long masterKeyId) {
|
||||||
@ -123,11 +123,8 @@ public class ProviderHelper {
|
|||||||
*/
|
*/
|
||||||
public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) {
|
public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) {
|
||||||
PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId);
|
PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId);
|
||||||
if (keyRing == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyRing.getPublicKey(keyId);
|
return (keyRing == null)? null : keyRing.getPublicKey(keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,11 +159,8 @@ public class ProviderHelper {
|
|||||||
*/
|
*/
|
||||||
public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) {
|
public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) {
|
||||||
PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId);
|
PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId);
|
||||||
if (keyRing == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyRing.getSecretKey(keyId);
|
return (keyRing == null) ? null : keyRing.getSecretKey(keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -411,10 +405,10 @@ public class ProviderHelper {
|
|||||||
long keyRingRowId, PGPSecretKey key, int rank) throws IOException {
|
long keyRingRowId, PGPSecretKey key, int rank) throws IOException {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
|
|
||||||
boolean has_private = true;
|
boolean hasPrivate = true;
|
||||||
if (key.isMasterKey()) {
|
if (key.isMasterKey()) {
|
||||||
if (PgpKeyHelper.isSecretKeyPrivateEmpty(key)) {
|
if (key.isPrivateKeyEmpty()) {
|
||||||
has_private = false;
|
hasPrivate = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,8 +416,8 @@ public class ProviderHelper {
|
|||||||
values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
|
values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
|
||||||
values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
|
values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
|
||||||
values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
|
values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
|
||||||
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && has_private));
|
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && hasPrivate));
|
||||||
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && has_private));
|
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && hasPrivate));
|
||||||
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
|
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
|
||||||
values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
|
values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
|
||||||
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
|
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
|
||||||
@ -480,6 +474,30 @@ public class ProviderHelper {
|
|||||||
return masterKeyIds;
|
return masterKeyIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private helper method
|
||||||
|
*/
|
||||||
|
private static ArrayList<Long> getKeyRingsRowIds(Context context, Uri queryUri) {
|
||||||
|
Cursor cursor = context.getContentResolver().query(queryUri,
|
||||||
|
new String[]{KeyRings._ID}, null, null, null);
|
||||||
|
|
||||||
|
ArrayList<Long> rowIds = new ArrayList<Long>();
|
||||||
|
if (cursor != null) {
|
||||||
|
int IdCol = cursor.getColumnIndex(KeyRings._ID);
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
rowIds.add(cursor.getLong(IdCol));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowIds;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves ids of all SecretKeyRings
|
* Retrieves ids of all SecretKeyRings
|
||||||
*/
|
*/
|
||||||
@ -496,6 +514,22 @@ public class ProviderHelper {
|
|||||||
return getKeyRingsMasterKeyIds(context, queryUri);
|
return getKeyRingsMasterKeyIds(context, queryUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ids of all SecretKeyRings
|
||||||
|
*/
|
||||||
|
public static ArrayList<Long> getSecretKeyRingsRowIds(Context context) {
|
||||||
|
Uri queryUri = KeyRings.buildSecretKeyRingsUri();
|
||||||
|
return getKeyRingsRowIds(context, queryUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ids of all PublicKeyRings
|
||||||
|
*/
|
||||||
|
public static ArrayList<Long> getPublicKeyRingsRowIds(Context context) {
|
||||||
|
Uri queryUri = KeyRings.buildPublicKeyRingsUri();
|
||||||
|
return getKeyRingsRowIds(context, queryUri);
|
||||||
|
}
|
||||||
|
|
||||||
public static void deletePublicKeyRing(Context context, long rowId) {
|
public static void deletePublicKeyRing(Context context, long rowId) {
|
||||||
ContentResolver cr = context.getContentResolver();
|
ContentResolver cr = context.getContentResolver();
|
||||||
cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null);
|
cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null);
|
||||||
@ -722,7 +756,7 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
String armoredKey = bos.toString("UTF-8");
|
String armoredKey = bos.toString("UTF-8");
|
||||||
|
|
||||||
Log.d(Constants.TAG, "armouredKey:" + armoredKey);
|
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
|
||||||
|
|
||||||
output.add(armoredKey);
|
output.add(armoredKey);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -44,11 +44,13 @@ import org.sufficientlysecure.keychain.helper.OtherHelper;
|
|||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
|
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
|
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||||
@ -152,6 +154,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
public static final String EXPORT_KEY_TYPE = "export_key_type";
|
public static final String EXPORT_KEY_TYPE = "export_key_type";
|
||||||
public static final String EXPORT_ALL = "export_all";
|
public static final String EXPORT_ALL = "export_all";
|
||||||
public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
|
public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
|
||||||
|
public static final String EXPORT_KEY_RING_ROW_ID = "export_key_rind_row_id";
|
||||||
|
|
||||||
// upload key
|
// upload key
|
||||||
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
|
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
|
||||||
@ -181,13 +184,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
// decrypt/verify
|
// decrypt/verify
|
||||||
public static final String RESULT_DECRYPTED_STRING = "decrypted_message";
|
public static final String RESULT_DECRYPTED_STRING = "decrypted_message";
|
||||||
public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
|
public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
|
||||||
public static final String RESULT_SIGNATURE = "signature";
|
public static final String RESULT_DECRYPT_VERIFY_RESULT = "signature";
|
||||||
public static final String RESULT_SIGNATURE_KEY_ID = "signature_key_id";
|
|
||||||
public static final String RESULT_SIGNATURE_USER_ID = "signature_user_id";
|
|
||||||
public static final String RESULT_CLEARTEXT_SIGNATURE_ONLY = "signature_only";
|
|
||||||
|
|
||||||
public static final String RESULT_SIGNATURE_SUCCESS = "signature_success";
|
|
||||||
public static final String RESULT_SIGNATURE_UNKNOWN = "signature_unknown";
|
|
||||||
|
|
||||||
// import
|
// import
|
||||||
public static final String RESULT_IMPORT_ADDED = "added";
|
public static final String RESULT_IMPORT_ADDED = "added";
|
||||||
@ -206,7 +203,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
private boolean mIsCanceled;
|
private boolean mIsCanceled;
|
||||||
|
|
||||||
public KeychainIntentService() {
|
public KeychainIntentService() {
|
||||||
super("ApgService");
|
super("KeychainIntentService");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -489,15 +486,17 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
// verifyText and decrypt returning additional resultData values for the
|
// verifyText and decrypt returning additional resultData values for the
|
||||||
// verification of signatures
|
// verification of signatures
|
||||||
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream);
|
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream);
|
||||||
builder.progress(this);
|
builder.progressDialogUpdater(this);
|
||||||
|
|
||||||
builder.assumeSymmetric(assumeSymmetricEncryption)
|
builder.assumeSymmetric(assumeSymmetricEncryption)
|
||||||
.passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
.passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
||||||
|
|
||||||
resultData = builder.build().execute();
|
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
|
||||||
|
|
||||||
outStream.close();
|
outStream.close();
|
||||||
|
|
||||||
|
resultData.putParcelable(RESULT_DECRYPT_VERIFY_RESULT, decryptVerifyResult);
|
||||||
|
|
||||||
/* Output */
|
/* Output */
|
||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
@ -599,13 +598,23 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
|
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
|
||||||
|
|
||||||
/* Operation */
|
/* Operation */
|
||||||
|
int keysTotal = 2;
|
||||||
|
int keysCreated = 0;
|
||||||
|
setProgress(
|
||||||
|
getApplicationContext().getResources().getQuantityString(R.plurals.progress_generating, keysTotal),
|
||||||
|
keysCreated,
|
||||||
|
keysTotal);
|
||||||
PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
|
PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
|
||||||
|
|
||||||
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
||||||
4096, passphrase, true);
|
4096, passphrase, true);
|
||||||
|
keysCreated++;
|
||||||
|
setProgress(keysCreated, keysTotal);
|
||||||
|
|
||||||
PGPSecretKey subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
PGPSecretKey subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
||||||
4096, passphrase, false);
|
4096, passphrase, false);
|
||||||
|
keysCreated++;
|
||||||
|
setProgress(keysCreated, keysTotal);
|
||||||
|
|
||||||
// TODO: default to one master for cert, one sub for encrypt and one sub
|
// TODO: default to one master for cert, one sub for encrypt and one sub
|
||||||
// for sign
|
// for sign
|
||||||
@ -668,10 +677,12 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
|
|
||||||
String outputFile = data.getString(EXPORT_FILENAME);
|
String outputFile = data.getString(EXPORT_FILENAME);
|
||||||
|
|
||||||
|
long[] rowIds = new long[0];
|
||||||
|
|
||||||
|
// If not exporting all keys get the rowIds of the keys to export from the intent
|
||||||
boolean exportAll = data.getBoolean(EXPORT_ALL);
|
boolean exportAll = data.getBoolean(EXPORT_ALL);
|
||||||
long keyRingMasterKeyId = -1;
|
|
||||||
if (!exportAll) {
|
if (!exportAll) {
|
||||||
keyRingMasterKeyId = data.getLong(EXPORT_KEY_RING_MASTER_KEY_ID);
|
rowIds = data.getLongArray(EXPORT_KEY_RING_ROW_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Operation */
|
/* Operation */
|
||||||
@ -684,24 +695,26 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
// OutputStream
|
// OutputStream
|
||||||
FileOutputStream outStream = new FileOutputStream(outputFile);
|
FileOutputStream outStream = new FileOutputStream(outputFile);
|
||||||
|
|
||||||
ArrayList<Long> keyRingMasterKeyIds = new ArrayList<Long>();
|
ArrayList<Long> keyRingRowIds = new ArrayList<Long>();
|
||||||
if (exportAll) {
|
if (exportAll) {
|
||||||
// get all key ring row ids based on export type
|
|
||||||
|
|
||||||
|
// get all key ring row ids based on export type
|
||||||
if (keyType == Id.type.public_key) {
|
if (keyType == Id.type.public_key) {
|
||||||
keyRingMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this);
|
keyRingRowIds = ProviderHelper.getPublicKeyRingsRowIds(this);
|
||||||
} else {
|
} else {
|
||||||
keyRingMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this);
|
keyRingRowIds = ProviderHelper.getSecretKeyRingsRowIds(this);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
keyRingMasterKeyIds.add(keyRingMasterKeyId);
|
for(long rowId : rowIds) {
|
||||||
|
keyRingRowIds.add(rowId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle resultData = new Bundle();
|
Bundle resultData;
|
||||||
|
|
||||||
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
|
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
|
||||||
resultData = pgpImportExport
|
resultData = pgpImportExport
|
||||||
.exportKeyRings(keyRingMasterKeyIds, keyType, outStream);
|
.exportKeyRings(keyRingRowIds, keyType, outStream);
|
||||||
|
|
||||||
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
|
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -751,7 +764,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
*/
|
*/
|
||||||
// need to have access to the bufferedInput, so we can reuse it for the possible
|
// need to have access to the bufferedInput, so we can reuse it for the possible
|
||||||
// PGPObject chunks after the first one, e.g. files with several consecutive ASCII
|
// PGPObject chunks after the first one, e.g. files with several consecutive ASCII
|
||||||
// armour blocks
|
// armor blocks
|
||||||
BufferedInputStream bufferedInput = new BufferedInputStream(new ByteArrayInputStream(downloadedKey));
|
BufferedInputStream bufferedInput = new BufferedInputStream(new ByteArrayInputStream(downloadedKey));
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -867,10 +880,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set progress of ProgressDialog by sending message to handler on UI thread
|
* Set progressDialogUpdater of ProgressDialog by sending message to handler on UI thread
|
||||||
*/
|
*/
|
||||||
public void setProgress(String message, int progress, int max) {
|
public void setProgress(String message, int progress, int max) {
|
||||||
Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
|
Log.d(Constants.TAG, "Send message by setProgress with progressDialogUpdater=" + progress + ", max="
|
||||||
+ max);
|
+ max);
|
||||||
|
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
|
@ -21,7 +21,6 @@ import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
|||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.DialogInterface.OnCancelListener;
|
import android.content.DialogInterface.OnCancelListener;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -51,21 +50,26 @@ public class KeychainIntentServiceHandler extends Handler {
|
|||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeychainIntentServiceHandler(Activity activity, ProgressDialogFragment progressDialogFragment) {
|
public KeychainIntentServiceHandler(Activity activity,
|
||||||
|
ProgressDialogFragment progressDialogFragment) {
|
||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
||||||
this.mProgressDialogFragment = progressDialogFragment;
|
this.mProgressDialogFragment = progressDialogFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) {
|
public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage,
|
||||||
this(activity, progressDialogMessageId, progressDialogStyle, false, null);
|
int progressDialogStyle) {
|
||||||
|
this(activity, progressDialogMessage, progressDialogStyle, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId,
|
public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage,
|
||||||
int progressDialogStyle, boolean cancelable,
|
int progressDialogStyle, boolean cancelable,
|
||||||
OnCancelListener onCancelListener) {
|
OnCancelListener onCancelListener) {
|
||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
||||||
this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId,
|
this.mProgressDialogFragment = ProgressDialogFragment.newInstance(
|
||||||
progressDialogStyle, cancelable, onCancelListener);
|
progressDialogMessage,
|
||||||
|
progressDialogStyle,
|
||||||
|
cancelable,
|
||||||
|
onCancelListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showProgressDialog(FragmentActivity activity) {
|
public void showProgressDialog(FragmentActivity activity) {
|
||||||
@ -84,43 +88,43 @@ public class KeychainIntentServiceHandler extends Handler {
|
|||||||
Bundle data = message.getData();
|
Bundle data = message.getData();
|
||||||
|
|
||||||
switch (message.arg1) {
|
switch (message.arg1) {
|
||||||
case MESSAGE_OKAY:
|
case MESSAGE_OKAY:
|
||||||
mProgressDialogFragment.dismissAllowingStateLoss();
|
mProgressDialogFragment.dismissAllowingStateLoss();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MESSAGE_EXCEPTION:
|
case MESSAGE_EXCEPTION:
|
||||||
mProgressDialogFragment.dismissAllowingStateLoss();
|
mProgressDialogFragment.dismissAllowingStateLoss();
|
||||||
|
|
||||||
// show error from service
|
// show error from service
|
||||||
if (data.containsKey(DATA_ERROR)) {
|
if (data.containsKey(DATA_ERROR)) {
|
||||||
Toast.makeText(mActivity,
|
Toast.makeText(mActivity,
|
||||||
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
|
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MESSAGE_UPDATE_PROGRESS:
|
|
||||||
if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) {
|
|
||||||
|
|
||||||
// update progress from service
|
|
||||||
if (data.containsKey(DATA_MESSAGE)) {
|
|
||||||
mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE),
|
|
||||||
data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
|
|
||||||
} else if (data.containsKey(DATA_MESSAGE_ID)) {
|
|
||||||
mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID),
|
|
||||||
data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
|
|
||||||
} else {
|
|
||||||
mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS),
|
|
||||||
data.getInt(DATA_PROGRESS_MAX));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case MESSAGE_UPDATE_PROGRESS:
|
||||||
break;
|
if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) {
|
||||||
|
|
||||||
|
// update progress from service
|
||||||
|
if (data.containsKey(DATA_MESSAGE)) {
|
||||||
|
mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE),
|
||||||
|
data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
|
||||||
|
} else if (data.containsKey(DATA_MESSAGE_ID)) {
|
||||||
|
mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID),
|
||||||
|
data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
|
||||||
|
} else {
|
||||||
|
mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS),
|
||||||
|
data.getInt(DATA_PROGRESS_MAX));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import java.util.Date;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import android.util.LongSparseArray;
|
||||||
import org.spongycastle.openpgp.PGPException;
|
import org.spongycastle.openpgp.PGPException;
|
||||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
@ -77,7 +78,7 @@ public class PassphraseCacheService extends Service {
|
|||||||
|
|
||||||
private BroadcastReceiver mIntentReceiver;
|
private BroadcastReceiver mIntentReceiver;
|
||||||
|
|
||||||
private HashMap<Long, String> mPassphraseCache = new HashMap<Long, String>();
|
private LongSparseArray<String> mPassphraseCache = new LongSparseArray<String>();
|
||||||
|
|
||||||
Context mContext;
|
Context mContext;
|
||||||
|
|
||||||
@ -347,7 +348,7 @@ public class PassphraseCacheService extends Service {
|
|||||||
Log.d(TAG, "Timeout of keyId " + keyId + ", removed from memory!");
|
Log.d(TAG, "Timeout of keyId " + keyId + ", removed from memory!");
|
||||||
|
|
||||||
// stop whole service if no cached passphrases remaining
|
// stop whole service if no cached passphrases remaining
|
||||||
if (mPassphraseCache.isEmpty()) {
|
if (mPassphraseCache.size() == 0) {
|
||||||
Log.d(TAG, "No passphrases remaining in memory, stopping service!");
|
Log.d(TAG, "No passphrases remaining in memory, stopping service!");
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ public class AppSettingsActivity extends ActionBarActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Inflate a "Done" custom action bar
|
// Inflate a "Done" custom action bar
|
||||||
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.api_settings_save,
|
ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.api_settings_save, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
@ -21,7 +21,6 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
@ -33,9 +32,11 @@ import org.spongycastle.util.Arrays;
|
|||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
|
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
@ -103,9 +104,8 @@ public class OpenPgpService extends RemoteService {
|
|||||||
|
|
||||||
// return PendingIntent to be executed by client
|
// return PendingIntent to be executed by client
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
|
||||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,8 +114,8 @@ public class OpenPgpService extends RemoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
|
||||||
result.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keyIdsArray);
|
result.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keyIdsArray);
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,9 +130,8 @@ public class OpenPgpService extends RemoteService {
|
|||||||
|
|
||||||
// return PendingIntent to be executed by client
|
// return PendingIntent to be executed by client
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
|
||||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,9 +178,9 @@ public class OpenPgpService extends RemoteService {
|
|||||||
return result;
|
return result;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||||
result.putExtra(OpenPgpApi.RESULT_ERRORS,
|
|
||||||
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,9 +207,9 @@ public class OpenPgpService extends RemoteService {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||||
result.putExtra(OpenPgpApi.RESULT_ERRORS,
|
|
||||||
new OpenPgpError(OpenPgpError.GENERIC_ERROR, "Missing parameter user_ids or key_ids!"));
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, "Missing parameter user_ids or key_ids!"));
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,9 +266,9 @@ public class OpenPgpService extends RemoteService {
|
|||||||
return result;
|
return result;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||||
result.putExtra(OpenPgpApi.RESULT_ERRORS,
|
|
||||||
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,98 +283,30 @@ public class OpenPgpService extends RemoteService {
|
|||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// TODO:
|
String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
|
||||||
// fix the mess: http://stackoverflow.com/questions/148130/how-do-i-peek-at-the-first-two-bytes-in-an-inputstream
|
|
||||||
// should we allow to decrypt everything under every key id or only the one set?
|
|
||||||
// TODO: instead of trying to get the passphrase before
|
|
||||||
// pause stream when passphrase is missing and then resume
|
|
||||||
|
|
||||||
// TODO: put this code into PgpDecryptVerify class
|
|
||||||
|
|
||||||
// TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
|
|
||||||
// app, Fix this?
|
|
||||||
// String passphrase = null;
|
|
||||||
// if (!signedOnly) {
|
|
||||||
// // BEGIN Get key
|
|
||||||
// // TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it
|
|
||||||
// // better!
|
|
||||||
// InputStream inputStream2 = new ByteArrayInputStream(inputBytes);
|
|
||||||
//
|
|
||||||
// // TODO: duplicates functions from DecryptActivity!
|
|
||||||
// long secretKeyId;
|
|
||||||
// try {
|
|
||||||
// if (inputStream2.markSupported()) {
|
|
||||||
// // should probably set this to the max size of two
|
|
||||||
// // pgpF objects, if it even needs to be anything other
|
|
||||||
// // than 0.
|
|
||||||
// inputStream2.mark(200);
|
|
||||||
// }
|
|
||||||
// secretKeyId = PgpHelper.getDecryptionKeyId(this, inputStream2);
|
|
||||||
// if (secretKeyId == Id.key.none) {
|
|
||||||
// throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
|
|
||||||
// }
|
|
||||||
// } catch (NoAsymmetricEncryptionException e) {
|
|
||||||
// if (inputStream2.markSupported()) {
|
|
||||||
// inputStream2.reset();
|
|
||||||
// }
|
|
||||||
// secretKeyId = Id.key.symmetric;
|
|
||||||
// if (!PgpDecryptVerify.hasSymmetricEncryption(this, inputStream2)) {
|
|
||||||
// throw new PgpGeneralException(
|
|
||||||
// getString(R.string.error_no_known_encryption_found));
|
|
||||||
// }
|
|
||||||
// // we do not support symmetric decryption from the API!
|
|
||||||
// throw new Exception("Symmetric decryption is not supported!");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
|
|
||||||
|
|
||||||
// NOTE: currently this only gets the passphrase for the key set for this client
|
|
||||||
String passphrase;
|
|
||||||
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
|
|
||||||
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
|
|
||||||
} else {
|
|
||||||
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
|
|
||||||
}
|
|
||||||
if (passphrase == null) {
|
|
||||||
// get PendingIntent for passphrase input, add it to given params and return to client
|
|
||||||
Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
|
|
||||||
return passphraseBundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
long inputLength = is.available();
|
long inputLength = is.available();
|
||||||
InputData inputData = new InputData(is, inputLength);
|
InputData inputData = new InputData(is, inputLength);
|
||||||
|
|
||||||
Bundle outputBundle;
|
|
||||||
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
|
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
|
||||||
|
builder.assumeSymmetric(false) // no support for symmetric encryption
|
||||||
builder.assumeSymmetric(false)
|
.enforcedKeyId(appSettings.getKeyId()) // allow only the private key for this app for decryption
|
||||||
.passphrase(passphrase);
|
.passphrase(passphrase);
|
||||||
|
|
||||||
// TODO: this also decrypts with other secret keys that have no passphrase!!!
|
// TODO: currently does not support binary signed-only content
|
||||||
outputBundle = builder.build().execute();
|
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
|
||||||
|
|
||||||
//TODO: instead of using all these wrapping use OpenPgpSignatureResult directly
|
if (decryptVerifyResult.isKeyPassphraseNeeded()) {
|
||||||
// in DecryptVerify class and then in DecryptActivity
|
// get PendingIntent for passphrase input, add it to given params and return to client
|
||||||
boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE, false);
|
Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
|
||||||
if (signature) {
|
return passphraseBundle;
|
||||||
long signatureKeyId = outputBundle
|
} else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) {
|
||||||
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, 0);
|
throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
|
||||||
String signatureUserId = outputBundle
|
}
|
||||||
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
|
|
||||||
boolean signatureSuccess = outputBundle
|
|
||||||
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
|
|
||||||
boolean signatureUnknown = outputBundle
|
|
||||||
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, false);
|
|
||||||
boolean signatureOnly = outputBundle
|
|
||||||
.getBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false);
|
|
||||||
|
|
||||||
int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
|
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
|
||||||
if (signatureSuccess) {
|
if (signatureResult != null) {
|
||||||
signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED;
|
if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY) {
|
||||||
} else if (signatureUnknown) {
|
// If signature is unknown we return an _additional_ PendingIntent
|
||||||
signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
|
|
||||||
|
|
||||||
// If signature is unknown we return an additional PendingIntent
|
|
||||||
// to retrieve the missing key
|
// to retrieve the missing key
|
||||||
// TODO!!!
|
// TODO!!!
|
||||||
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||||
@ -389,11 +320,9 @@ public class OpenPgpService extends RemoteService {
|
|||||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
|
||||||
OpenPgpSignatureResult sigResult = new OpenPgpSignatureResult(signatureStatus,
|
|
||||||
signatureUserId, signatureOnly, signatureKeyId);
|
|
||||||
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, sigResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
is.close();
|
is.close();
|
||||||
os.close();
|
os.close();
|
||||||
@ -403,9 +332,44 @@ public class OpenPgpService extends RemoteService {
|
|||||||
return result;
|
return result;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||||
result.putExtra(OpenPgpApi.RESULT_ERRORS,
|
|
||||||
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent getKeyImpl(Intent data) {
|
||||||
|
try {
|
||||||
|
long keyId = data.getLongExtra(OpenPgpApi.EXTRA_KEY_ID, 0);
|
||||||
|
|
||||||
|
if (ProviderHelper.getPGPPublicKeyByKeyId(this, keyId) == null) {
|
||||||
|
Intent result = new Intent();
|
||||||
|
|
||||||
|
// If keys are not in db we return an additional PendingIntent
|
||||||
|
// to retrieve the missing key
|
||||||
|
// TODO!!!
|
||||||
|
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||||
|
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||||
|
|
||||||
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
|
||||||
|
PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
|
||||||
|
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
Intent result = new Intent();
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Intent result = new Intent();
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||||
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,7 +395,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
if (data == null) {
|
if (data == null) {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
OpenPgpError error = new OpenPgpError(OpenPgpError.GENERIC_ERROR, "params Bundle required!");
|
OpenPgpError error = new OpenPgpError(OpenPgpError.GENERIC_ERROR, "params Bundle required!");
|
||||||
result.putExtra(OpenPgpApi.RESULT_ERRORS, error);
|
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -440,7 +404,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) {
|
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
OpenPgpError error = new OpenPgpError(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
|
OpenPgpError error = new OpenPgpError(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
|
||||||
result.putExtra(OpenPgpApi.RESULT_ERRORS, error);
|
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -471,13 +435,12 @@ public class OpenPgpService extends RemoteService {
|
|||||||
return signImpl(data, input, output, appSettings);
|
return signImpl(data, input, output, appSettings);
|
||||||
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
|
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
|
||||||
return encryptAndSignImpl(data, input, output, appSettings, false);
|
return encryptAndSignImpl(data, input, output, appSettings, false);
|
||||||
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCTYPT.equals(action)) {
|
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
|
||||||
return encryptAndSignImpl(data, input, output, appSettings, true);
|
return encryptAndSignImpl(data, input, output, appSettings, true);
|
||||||
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
|
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
|
||||||
return decryptAndVerifyImpl(data, input, output, appSettings);
|
return decryptAndVerifyImpl(data, input, output, appSettings);
|
||||||
} else if (OpenPgpApi.ACTION_DOWNLOAD_KEYS.equals(action)) {
|
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
|
||||||
// TODO!
|
return getKeyImpl(data);
|
||||||
return null;
|
|
||||||
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
|
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
|
||||||
return getKeyIdsImpl(data);
|
return getKeyIdsImpl(data);
|
||||||
} else {
|
} else {
|
||||||
|
@ -72,7 +72,7 @@ public abstract class RemoteService extends Service {
|
|||||||
// return error
|
// return error
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
result.putExtra(OpenPgpApi.RESULT_ERRORS,
|
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||||
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
|
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
|
||||||
|
|
||||||
// Inflate a "Done"/"Cancel" custom action bar view
|
// Inflate a "Done"/"Cancel" custom action bar view
|
||||||
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.api_register_allow,
|
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.api_register_allow, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@ -108,7 +108,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
RemoteServiceActivity.this.finish();
|
RemoteServiceActivity.this.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, R.string.api_register_disallow, new View.OnClickListener() {
|
}, R.string.api_register_disallow, R.drawable.ic_action_cancel, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// Disallow
|
// Disallow
|
||||||
@ -161,7 +161,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Inflate a "Done"/"Cancel" custom action bar view
|
// Inflate a "Done"/"Cancel" custom action bar view
|
||||||
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
|
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@ -173,7 +173,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
||||||
RemoteServiceActivity.this.finish();
|
RemoteServiceActivity.this.finish();
|
||||||
}
|
}
|
||||||
}, R.string.btn_do_not_save, new View.OnClickListener() {
|
}, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// cancel
|
// cancel
|
||||||
@ -214,7 +214,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
String text = "<font color=\"red\">" + errorMessage + "</font>";
|
String text = "<font color=\"red\">" + errorMessage + "</font>";
|
||||||
|
|
||||||
// Inflate a "Done" custom action bar view
|
// Inflate a "Done" custom action bar view
|
||||||
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_okay,
|
ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -230,7 +230,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
|
|
||||||
// Message is received after signing is done in ApgService
|
// Message is received after signing is done in ApgService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_signing, ProgressDialog.STYLE_SPINNER) {
|
getString(R.string.progress_signing), ProgressDialog.STYLE_SPINNER) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard ApgHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
@ -249,7 +249,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
@ -283,7 +283,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
|
|
||||||
// Message is received after uploading is done in ApgService
|
// Message is received after uploading is done in ApgService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
|
getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard ApgHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
@ -295,7 +295,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
|
@ -25,6 +25,7 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
@ -32,6 +33,7 @@ import org.sufficientlysecure.keychain.R;
|
|||||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||||
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
||||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||||
@ -528,6 +530,10 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
Log.e(Constants.TAG, "File not found!", e);
|
Log.e(Constants.TAG, "File not found!", e);
|
||||||
AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
|
AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
|
||||||
AppMsg.STYLE_ALERT).show();
|
AppMsg.STYLE_ALERT).show();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (inStream != null) inStream.close();
|
||||||
|
} catch (Exception e){ }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes());
|
inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes());
|
||||||
@ -644,7 +650,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
// Message is received after encrypting is done in ApgService
|
// Message is received after encrypting is done in ApgService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_decrypting, ProgressDialog.STYLE_HORIZONTAL) {
|
getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard ApgHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
@ -690,11 +696,15 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE)) {
|
PgpDecryptVerifyResult decryptVerifyResult =
|
||||||
String userId = returnData
|
returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
|
||||||
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
|
|
||||||
mSignatureKeyId = returnData
|
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
|
||||||
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
|
|
||||||
|
if (signatureResult != null) {
|
||||||
|
|
||||||
|
String userId = signatureResult.getUserId();
|
||||||
|
mSignatureKeyId = signatureResult.getKeyId();
|
||||||
mUserIdRest.setText("id: "
|
mUserIdRest.setText("id: "
|
||||||
+ PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId));
|
+ PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId));
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
@ -707,26 +717,37 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
mUserId.setText(userId);
|
mUserId.setText(userId);
|
||||||
|
|
||||||
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS)) {
|
switch (signatureResult.getStatus()) {
|
||||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
|
case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {
|
||||||
mLookupKey.setVisibility(View.GONE);
|
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
|
||||||
} else if (returnData
|
mLookupKey.setVisibility(View.GONE);
|
||||||
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) {
|
break;
|
||||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
}
|
||||||
mLookupKey.setVisibility(View.VISIBLE);
|
|
||||||
AppMsg.makeText(DecryptActivity.this,
|
// TODO!
|
||||||
R.string.unknown_signature,
|
// case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {
|
||||||
AppMsg.STYLE_ALERT).show();
|
// break;
|
||||||
} else {
|
// }
|
||||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
|
||||||
mLookupKey.setVisibility(View.GONE);
|
case OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY: {
|
||||||
|
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||||
|
mLookupKey.setVisibility(View.VISIBLE);
|
||||||
|
AppMsg.makeText(DecryptActivity.this,
|
||||||
|
R.string.unknown_signature,
|
||||||
|
AppMsg.STYLE_ALERT).show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||||
|
mLookupKey.setVisibility(View.GONE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mSignatureLayout.setVisibility(View.VISIBLE);
|
mSignatureLayout.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
|
@ -92,12 +92,12 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
private SectionView mUserIdsView;
|
private SectionView mUserIdsView;
|
||||||
private SectionView mKeysView;
|
private SectionView mKeysView;
|
||||||
|
|
||||||
private String mCurrentPassPhrase = null;
|
private String mCurrentPassphrase = null;
|
||||||
private String mNewPassPhrase = null;
|
private String mNewPassPhrase = null;
|
||||||
private String mSavedNewPassPhrase = null;
|
private String mSavedNewPassPhrase = null;
|
||||||
private boolean mIsPassPhraseSet;
|
private boolean mIsPassPhraseSet;
|
||||||
|
|
||||||
private BootstrapButton mChangePassPhrase;
|
private BootstrapButton mChangePassphrase;
|
||||||
|
|
||||||
private CheckBox mNoPassphrase;
|
private CheckBox mNoPassphrase;
|
||||||
|
|
||||||
@ -134,14 +134,14 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
* @param intent
|
* @param intent
|
||||||
*/
|
*/
|
||||||
private void handleActionCreateKey(Intent intent) {
|
private void handleActionCreateKey(Intent intent) {
|
||||||
// Inflate a "Done"/"Cancel" custom action bar
|
// Inflate a "Save"/"Cancel" custom action bar
|
||||||
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_save,
|
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
saveClicked();
|
saveClicked();
|
||||||
}
|
}
|
||||||
}, R.string.btn_do_not_save, new View.OnClickListener() {
|
}, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
cancelClicked();
|
cancelClicked();
|
||||||
@ -150,7 +150,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
Bundle extras = intent.getExtras();
|
Bundle extras = intent.getExtras();
|
||||||
|
|
||||||
mCurrentPassPhrase = "";
|
mCurrentPassphrase = "";
|
||||||
|
|
||||||
if (extras != null) {
|
if (extras != null) {
|
||||||
// if userId is given, prefill the fields
|
// if userId is given, prefill the fields
|
||||||
@ -165,7 +165,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
if (noPassphrase) {
|
if (noPassphrase) {
|
||||||
// check "no passphrase" checkbox and remove button
|
// check "no passphrase" checkbox and remove button
|
||||||
mNoPassphrase.setChecked(true);
|
mNoPassphrase.setChecked(true);
|
||||||
mChangePassPhrase.setVisibility(View.GONE);
|
mChangePassphrase.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,13 +181,14 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
// fill values for this action
|
// fill values for this action
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE,
|
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE,
|
||||||
mCurrentPassPhrase);
|
mCurrentPassphrase);
|
||||||
|
|
||||||
serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after generating is done in ApgService
|
// Message is received after generating is done in ApgService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
||||||
this, R.string.progress_generating, ProgressDialog.STYLE_SPINNER, true,
|
this, getResources().getQuantityString(R.plurals.progress_generating, 1),
|
||||||
|
ProgressDialog.STYLE_HORIZONTAL, true,
|
||||||
|
|
||||||
new DialogInterface.OnCancelListener() {
|
new DialogInterface.OnCancelListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -224,7 +225,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
buildLayout();
|
buildLayout();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
@ -248,8 +249,8 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
* @param intent
|
* @param intent
|
||||||
*/
|
*/
|
||||||
private void handleActionEditKey(Intent intent) {
|
private void handleActionEditKey(Intent intent) {
|
||||||
// Inflate a "Done"/"Cancel" custom action bar
|
// Inflate a "Save"/"Cancel" custom action bar
|
||||||
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_save,
|
ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@ -279,9 +280,9 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
|
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
|
||||||
String passPhrase = PassphraseCacheService.getCachedPassphrase(
|
String passphrase = PassphraseCacheService.getCachedPassphrase(
|
||||||
EditKeyActivity.this, masterKeyId);
|
EditKeyActivity.this, masterKeyId);
|
||||||
mCurrentPassPhrase = passPhrase;
|
mCurrentPassphrase = passphrase;
|
||||||
finallySaveClicked();
|
finallySaveClicked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,8 +327,8 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
cancelClicked();
|
cancelClicked();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_key_edit_export_file:
|
case R.id.menu_key_edit_export_file:
|
||||||
mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
|
long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
|
||||||
+ "/secexport.asc");
|
mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.path.APP_DIR_FILE_SEC);
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_key_edit_delete: {
|
case R.id.menu_key_edit_delete: {
|
||||||
// Message is received after key is deleted
|
// Message is received after key is deleted
|
||||||
@ -371,14 +372,14 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mCurrentPassPhrase = "";
|
mCurrentPassphrase = "";
|
||||||
|
|
||||||
buildLayout();
|
buildLayout();
|
||||||
mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
|
mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
|
||||||
if (!mIsPassPhraseSet) {
|
if (!mIsPassPhraseSet) {
|
||||||
// check "no passphrase" checkbox and remove button
|
// check "no passphrase" checkbox and remove button
|
||||||
mNoPassphrase.setChecked(true);
|
mNoPassphrase.setChecked(true);
|
||||||
mChangePassPhrase.setVisibility(View.GONE);
|
mChangePassphrase.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +409,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
// set title based on isPassphraseSet()
|
// set title based on isPassphraseSet()
|
||||||
int title = -1;
|
int title = -1;
|
||||||
if (isPassphraseSet()) {
|
if (isPassphraseSet()) {
|
||||||
title = R.string.title_change_pass_phrase;
|
title = R.string.title_change_passphrase;
|
||||||
} else {
|
} else {
|
||||||
title = R.string.title_set_passphrase;
|
title = R.string.title_set_passphrase;
|
||||||
}
|
}
|
||||||
@ -427,7 +428,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
setContentView(R.layout.edit_key_activity);
|
setContentView(R.layout.edit_key_activity);
|
||||||
|
|
||||||
// find views
|
// find views
|
||||||
mChangePassPhrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_pass_phrase);
|
mChangePassphrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_passphrase);
|
||||||
mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
|
mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
|
||||||
|
|
||||||
// Build layout based on given userIds and keys
|
// Build layout based on given userIds and keys
|
||||||
@ -447,7 +448,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
updatePassPhraseButtonText();
|
updatePassPhraseButtonText();
|
||||||
|
|
||||||
mChangePassPhrase.setOnClickListener(new OnClickListener() {
|
mChangePassphrase.setOnClickListener(new OnClickListener() {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
showSetPassphraseDialog();
|
showSetPassphraseDialog();
|
||||||
}
|
}
|
||||||
@ -462,10 +463,10 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
// remove passphrase
|
// remove passphrase
|
||||||
mSavedNewPassPhrase = mNewPassPhrase;
|
mSavedNewPassPhrase = mNewPassPhrase;
|
||||||
mNewPassPhrase = "";
|
mNewPassPhrase = "";
|
||||||
mChangePassPhrase.setVisibility(View.GONE);
|
mChangePassphrase.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
mNewPassPhrase = mSavedNewPassPhrase;
|
mNewPassPhrase = mSavedNewPassPhrase;
|
||||||
mChangePassPhrase.setVisibility(View.VISIBLE);
|
mChangePassphrase.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -504,7 +505,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
showPassphraseDialog(masterKeyId, masterCanSign);
|
showPassphraseDialog(masterKeyId, masterCanSign);
|
||||||
} else {
|
} else {
|
||||||
mCurrentPassPhrase = passphrase;
|
mCurrentPassphrase = passphrase;
|
||||||
finallySaveClicked();
|
finallySaveClicked();
|
||||||
}
|
}
|
||||||
} catch (PgpGeneralException e) {
|
} catch (PgpGeneralException e) {
|
||||||
@ -523,7 +524,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
// fill values for this action
|
// fill values for this action
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
data.putString(KeychainIntentService.SAVE_KEYRING_CURRENT_PASSPHRASE,
|
data.putString(KeychainIntentService.SAVE_KEYRING_CURRENT_PASSPHRASE,
|
||||||
mCurrentPassPhrase);
|
mCurrentPassphrase);
|
||||||
data.putString(KeychainIntentService.SAVE_KEYRING_NEW_PASSPHRASE, mNewPassPhrase);
|
data.putString(KeychainIntentService.SAVE_KEYRING_NEW_PASSPHRASE, mNewPassPhrase);
|
||||||
data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_USER_IDS,
|
data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_USER_IDS,
|
||||||
getUserIds(mUserIdsView));
|
getUserIds(mUserIdsView));
|
||||||
@ -541,7 +542,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
// Message is received after saving is done in ApgService
|
// Message is received after saving is done in ApgService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_saving, ProgressDialog.STYLE_HORIZONTAL) {
|
getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard ApgHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
@ -559,7 +560,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
setResult(RESULT_OK, data);
|
setResult(RESULT_OK, data);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
@ -694,7 +695,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updatePassPhraseButtonText() {
|
private void updatePassPhraseButtonText() {
|
||||||
mChangePassPhrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase)
|
mChangePassphrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase)
|
||||||
: getString(R.string.btn_set_passphrase));
|
: getString(R.string.btn_set_passphrase));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,17 +52,21 @@ import android.os.Message;
|
|||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.animation.AlphaAnimation;
|
||||||
|
import android.view.animation.Animation;
|
||||||
import android.view.animation.AnimationUtils;
|
import android.view.animation.AnimationUtils;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import android.widget.ViewFlipper;
|
import android.widget.ViewFlipper;
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
|
import com.beardedhen.androidbootstrap.FontAwesomeText;
|
||||||
import com.devspark.appmsg.AppMsg;
|
import com.devspark.appmsg.AppMsg;
|
||||||
|
|
||||||
public class EncryptActivity extends DrawerActivity {
|
public class EncryptActivity extends DrawerActivity {
|
||||||
@ -101,18 +105,24 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
private int mEncryptTarget;
|
private int mEncryptTarget;
|
||||||
|
|
||||||
private EditText mPassPhrase = null;
|
private EditText mPassphrase = null;
|
||||||
private EditText mPassPhraseAgain = null;
|
private EditText mPassphraseAgain = null;
|
||||||
private CheckBox mAsciiArmor = null;
|
private CheckBox mAsciiArmor = null;
|
||||||
private Spinner mFileCompression = null;
|
private Spinner mFileCompression = null;
|
||||||
|
|
||||||
private EditText mFilename = null;
|
private EditText mFilename = null;
|
||||||
private CheckBox mDeleteAfter = null;
|
private CheckBox mDeleteAfter = null;
|
||||||
|
private CheckBox mShareAfter = null;
|
||||||
private BootstrapButton mBrowse = null;
|
private BootstrapButton mBrowse = null;
|
||||||
|
|
||||||
private String mInputFilename = null;
|
private String mInputFilename = null;
|
||||||
private String mOutputFilename = null;
|
private String mOutputFilename = null;
|
||||||
|
|
||||||
|
private Integer mShortAnimationDuration = null;
|
||||||
|
private boolean mFileAdvancedSettingsVisible = false;
|
||||||
|
private TextView mFileAdvancedSettings = null;
|
||||||
|
private LinearLayout mFileAdvancedSettingsContainer = null;
|
||||||
|
private FontAwesomeText mAdvancedSettingsIcon;
|
||||||
private boolean mAsciiArmorDemand = false;
|
private boolean mAsciiArmorDemand = false;
|
||||||
private boolean mOverrideAsciiArmor = false;
|
private boolean mOverrideAsciiArmor = false;
|
||||||
|
|
||||||
@ -147,6 +157,9 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
updateMode();
|
updateMode();
|
||||||
|
|
||||||
updateActionBarButtons();
|
updateActionBarButtons();
|
||||||
|
|
||||||
|
// retrieve and cache the system's short animation time
|
||||||
|
mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -436,14 +449,14 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
// symmetric encryption
|
// symmetric encryption
|
||||||
if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
|
if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
|
||||||
boolean gotPassPhrase = false;
|
boolean gotPassPhrase = false;
|
||||||
String passPhrase = mPassPhrase.getText().toString();
|
String passphrase = mPassphrase.getText().toString();
|
||||||
String passPhraseAgain = mPassPhraseAgain.getText().toString();
|
String passphraseAgain = mPassphraseAgain.getText().toString();
|
||||||
if (!passPhrase.equals(passPhraseAgain)) {
|
if (!passphrase.equals(passphraseAgain)) {
|
||||||
AppMsg.makeText(this, R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
|
AppMsg.makeText(this, R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gotPassPhrase = (passPhrase.length() != 0);
|
gotPassPhrase = (passphrase.length() != 0);
|
||||||
if (!gotPassPhrase) {
|
if (!gotPassPhrase) {
|
||||||
AppMsg.makeText(this, R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
|
AppMsg.makeText(this, R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
|
||||||
.show();
|
.show();
|
||||||
@ -550,11 +563,11 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
|
if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
|
||||||
Log.d(Constants.TAG, "Symmetric encryption enabled!");
|
Log.d(Constants.TAG, "Symmetric encryption enabled!");
|
||||||
String passPhrase = mPassPhrase.getText().toString();
|
String passphrase = mPassphrase.getText().toString();
|
||||||
if (passPhrase.length() == 0) {
|
if (passphrase.length() == 0) {
|
||||||
passPhrase = null;
|
passphrase = null;
|
||||||
}
|
}
|
||||||
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passPhrase);
|
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase);
|
||||||
} else {
|
} else {
|
||||||
mSecretKeyIdToPass = mSecretKeyId;
|
mSecretKeyIdToPass = mSecretKeyId;
|
||||||
encryptionKeyIds = mEncryptionKeyIds;
|
encryptionKeyIds = mEncryptionKeyIds;
|
||||||
@ -604,7 +617,7 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
// Message is received after encrypting is done in ApgService
|
// Message is received after encrypting is done in ApgService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_encrypting, ProgressDialog.STYLE_HORIZONTAL) {
|
getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard ApgHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
@ -650,6 +663,15 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
.newInstance(mInputFilename);
|
.newInstance(mInputFilename);
|
||||||
deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
|
deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mShareAfter.isChecked()) {
|
||||||
|
// Share encrypted file
|
||||||
|
Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
|
||||||
|
sendFileIntent.setType("*/*");
|
||||||
|
sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
|
||||||
|
startActivity(Intent.createChooser(sendFileIntent,
|
||||||
|
getString(R.string.title_send_file)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -659,8 +681,6 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
@ -766,8 +786,8 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
mMainUserId = (TextView) findViewById(R.id.mainUserId);
|
mMainUserId = (TextView) findViewById(R.id.mainUserId);
|
||||||
mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
|
mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
|
||||||
|
|
||||||
mPassPhrase = (EditText) findViewById(R.id.passPhrase);
|
mPassphrase = (EditText) findViewById(R.id.passphrase);
|
||||||
mPassPhraseAgain = (EditText) findViewById(R.id.passPhraseAgain);
|
mPassphraseAgain = (EditText) findViewById(R.id.passphraseAgain);
|
||||||
|
|
||||||
// measure the height of the source_file view and set the message view's min height to that,
|
// measure the height of the source_file view and set the message view's min height to that,
|
||||||
// so it fills mSource fully... bit of a hack.
|
// so it fills mSource fully... bit of a hack.
|
||||||
@ -785,6 +805,50 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mAdvancedSettingsIcon = (FontAwesomeText) findViewById(R.id.advancedSettingsIcon);
|
||||||
|
mFileAdvancedSettingsContainer = (LinearLayout) findViewById(R.id.fileAdvancedSettingsContainer);
|
||||||
|
mFileAdvancedSettings = (TextView) findViewById(R.id.advancedSettings);
|
||||||
|
|
||||||
|
LinearLayout advancedSettingsControl = (LinearLayout) findViewById(R.id.advancedSettingsControl);
|
||||||
|
advancedSettingsControl.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
mFileAdvancedSettingsVisible = !mFileAdvancedSettingsVisible;
|
||||||
|
if (mFileAdvancedSettingsVisible) {
|
||||||
|
mAdvancedSettingsIcon.setIcon("fa-chevron-down");
|
||||||
|
mFileAdvancedSettingsContainer.setVisibility(View.VISIBLE);
|
||||||
|
AlphaAnimation animation = new AlphaAnimation(0f, 1f);
|
||||||
|
animation.setDuration(mShortAnimationDuration);
|
||||||
|
mFileAdvancedSettingsContainer.startAnimation(animation);
|
||||||
|
mFileAdvancedSettings.setText(R.string.btn_encryption_advanced_settings_hide);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
mAdvancedSettingsIcon.setIcon("fa-chevron-right");
|
||||||
|
AlphaAnimation animation = new AlphaAnimation(1f, 0f);
|
||||||
|
animation.setDuration(mShortAnimationDuration);
|
||||||
|
animation.setAnimationListener(new Animation.AnimationListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationStart(Animation animation) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animation animation) {
|
||||||
|
// making sure that at the end the container is completely removed from view
|
||||||
|
mFileAdvancedSettingsContainer.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationRepeat(Animation animation) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mFileAdvancedSettingsContainer.startAnimation(animation);
|
||||||
|
mFileAdvancedSettings.setText(R.string.btn_encryption_advanced_settings_show);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
mFileCompression = (Spinner) findViewById(R.id.fileCompression);
|
mFileCompression = (Spinner) findViewById(R.id.fileCompression);
|
||||||
Choice[] choices = new Choice[]{
|
Choice[] choices = new Choice[]{
|
||||||
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
|
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
|
||||||
@ -809,6 +873,7 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption);
|
mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption);
|
||||||
|
mShareAfter = (CheckBox) findViewById(R.id.shareAfterEncryption);
|
||||||
|
|
||||||
mAsciiArmor = (CheckBox) findViewById(R.id.asciiArmour);
|
mAsciiArmor = (CheckBox) findViewById(R.id.asciiArmour);
|
||||||
mAsciiArmor.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour());
|
mAsciiArmor.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour());
|
||||||
|
@ -22,16 +22,11 @@ import java.util.ArrayList;
|
|||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
|
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
|
||||||
import android.support.v4.app.FragmentTransaction;
|
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
public class HelpActivity extends ActionBarActivity {
|
public class HelpActivity extends ActionBarActivity {
|
||||||
public static final String EXTRA_SELECTED_TAB = "selectedTab";
|
public static final String EXTRA_SELECTED_TAB = "selectedTab";
|
||||||
@ -64,19 +59,19 @@ public class HelpActivity extends ActionBarActivity {
|
|||||||
Bundle startBundle = new Bundle();
|
Bundle startBundle = new Bundle();
|
||||||
startBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_start);
|
startBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_start);
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_start)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_start)),
|
||||||
HelpHtmlFragment.class, startBundle, (selectedTab == 0 ? true : false));
|
HelpHtmlFragment.class, startBundle, (selectedTab == 0) );
|
||||||
|
|
||||||
Bundle nfcBundle = new Bundle();
|
Bundle nfcBundle = new Bundle();
|
||||||
nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_nfc_beam);
|
nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_nfc_beam);
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
|
||||||
HelpHtmlFragment.class, nfcBundle, (selectedTab == 1 ? true : false));
|
HelpHtmlFragment.class, nfcBundle, (selectedTab == 1) );
|
||||||
|
|
||||||
Bundle changelogBundle = new Bundle();
|
Bundle changelogBundle = new Bundle();
|
||||||
changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_changelog);
|
changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_changelog);
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_changelog)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_changelog)),
|
||||||
HelpHtmlFragment.class, changelogBundle, (selectedTab == 2 ? true : false));
|
HelpHtmlFragment.class, changelogBundle, (selectedTab == 2) );
|
||||||
|
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
|
||||||
HelpAboutFragment.class, null, (selectedTab == 3 ? true : false));
|
HelpAboutFragment.class, null, (selectedTab == 3) );
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -161,7 +161,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
|
|||||||
} else if (extras.containsKey(EXTRA_KEY_ID)) {
|
} else if (extras.containsKey(EXTRA_KEY_ID)) {
|
||||||
long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
|
long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
|
||||||
if (keyId != 0) {
|
if (keyId != 0) {
|
||||||
query = "0x" + PgpKeyHelper.convertKeyToHex(keyId);
|
query = PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||||
}
|
}
|
||||||
} else if (extras.containsKey(EXTRA_FINGERPRINT)) {
|
} else if (extras.containsKey(EXTRA_FINGERPRINT)) {
|
||||||
String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT);
|
String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT);
|
||||||
@ -360,51 +360,53 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
// Message is received after importing is done in ApgService
|
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
|
||||||
R.string.progress_importing, ProgressDialog.STYLE_HORIZONTAL) {
|
|
||||||
public void handleMessage(Message message) {
|
|
||||||
// handle messages by standard ApgHandler first
|
|
||||||
super.handleMessage(message);
|
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
|
||||||
// get returned data bundle
|
|
||||||
Bundle returnData = message.getData();
|
|
||||||
|
|
||||||
int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED);
|
|
||||||
int updated = returnData
|
|
||||||
.getInt(KeychainIntentService.RESULT_IMPORT_UPDATED);
|
|
||||||
int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD);
|
|
||||||
String toastMessage;
|
|
||||||
if (added > 0 && updated > 0) {
|
|
||||||
String addedStr = getResources().getQuantityString(
|
|
||||||
R.plurals.keys_added_and_updated_1, added, added);
|
|
||||||
String updatedStr = getResources().getQuantityString(
|
|
||||||
R.plurals.keys_added_and_updated_2, updated, updated);
|
|
||||||
toastMessage = addedStr + updatedStr;
|
|
||||||
} else if (added > 0) {
|
|
||||||
toastMessage = getResources().getQuantityString(R.plurals.keys_added,
|
|
||||||
added, added);
|
|
||||||
} else if (updated > 0) {
|
|
||||||
toastMessage = getResources().getQuantityString(R.plurals.keys_updated,
|
|
||||||
updated, updated);
|
|
||||||
} else {
|
|
||||||
toastMessage = getString(R.string.no_keys_added_or_updated);
|
|
||||||
}
|
|
||||||
AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO)
|
|
||||||
.show();
|
|
||||||
if (bad > 0) {
|
|
||||||
BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad);
|
|
||||||
badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import keys with mImportData
|
* Import keys with mImportData
|
||||||
*/
|
*/
|
||||||
public void importKeys() {
|
public void importKeys() {
|
||||||
|
// Message is received after importing is done in ApgService
|
||||||
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
||||||
|
this,
|
||||||
|
getString(R.string.progress_importing),
|
||||||
|
ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
|
public void handleMessage(Message message) {
|
||||||
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
|
super.handleMessage(message);
|
||||||
|
|
||||||
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
|
// get returned data bundle
|
||||||
|
Bundle returnData = message.getData();
|
||||||
|
|
||||||
|
int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED);
|
||||||
|
int updated = returnData
|
||||||
|
.getInt(KeychainIntentService.RESULT_IMPORT_UPDATED);
|
||||||
|
int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD);
|
||||||
|
String toastMessage;
|
||||||
|
if (added > 0 && updated > 0) {
|
||||||
|
String addedStr = getResources().getQuantityString(
|
||||||
|
R.plurals.keys_added_and_updated_1, added, added);
|
||||||
|
String updatedStr = getResources().getQuantityString(
|
||||||
|
R.plurals.keys_added_and_updated_2, updated, updated);
|
||||||
|
toastMessage = addedStr + updatedStr;
|
||||||
|
} else if (added > 0) {
|
||||||
|
toastMessage = getResources().getQuantityString(R.plurals.keys_added,
|
||||||
|
added, added);
|
||||||
|
} else if (updated > 0) {
|
||||||
|
toastMessage = getResources().getQuantityString(R.plurals.keys_updated,
|
||||||
|
updated, updated);
|
||||||
|
} else {
|
||||||
|
toastMessage = getString(R.string.no_keys_added_or_updated);
|
||||||
|
}
|
||||||
|
AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO)
|
||||||
|
.show();
|
||||||
|
if (bad > 0) {
|
||||||
|
BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad);
|
||||||
|
badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (mListFragment.getKeyBytes() != null || mListFragment.getDataUri() != null) {
|
if (mListFragment.getKeyBytes() != null || mListFragment.getDataUri() != null) {
|
||||||
Log.d(Constants.TAG, "importKeys started");
|
Log.d(Constants.TAG, "importKeys started");
|
||||||
|
|
||||||
|
@ -219,27 +219,44 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
} else {
|
} else {
|
||||||
setListShownNoAnimation(true);
|
setListShownNoAnimation(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Exception error = data.getError();
|
||||||
|
|
||||||
switch (loader.getId()) {
|
switch (loader.getId()) {
|
||||||
case LOADER_ID_BYTES:
|
case LOADER_ID_BYTES:
|
||||||
|
|
||||||
|
if(error == null){
|
||||||
|
// No error
|
||||||
|
} else if(error instanceof ImportKeysListLoader.FileHasNoContent) {
|
||||||
|
AppMsg.makeText(getActivity(), R.string.error_import_file_no_content,
|
||||||
|
AppMsg.STYLE_ALERT).show();
|
||||||
|
} else if(error instanceof ImportKeysListLoader.NonPgpPart) {
|
||||||
|
AppMsg.makeText(getActivity(),
|
||||||
|
((ImportKeysListLoader.NonPgpPart) error).getCount() + " " + getResources().
|
||||||
|
getQuantityString(R.plurals.error_import_non_pgp_part,
|
||||||
|
((ImportKeysListLoader.NonPgpPart) error).getCount()),
|
||||||
|
new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.confirm)).show();
|
||||||
|
} else {
|
||||||
|
AppMsg.makeText(getActivity(), R.string.error_generic_report_bug,
|
||||||
|
new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.alert)).show();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LOADER_ID_SERVER_QUERY:
|
case LOADER_ID_SERVER_QUERY:
|
||||||
|
|
||||||
Exception error = data.getError();
|
if(error == null) {
|
||||||
|
|
||||||
if(error == null){
|
|
||||||
AppMsg.makeText(
|
AppMsg.makeText(
|
||||||
getActivity(), getResources().getQuantityString(R.plurals.keys_found,
|
getActivity(), getResources().getQuantityString(R.plurals.keys_found,
|
||||||
mAdapter.getCount(), mAdapter.getCount()),
|
mAdapter.getCount(), mAdapter.getCount()),
|
||||||
AppMsg.STYLE_INFO
|
AppMsg.STYLE_INFO
|
||||||
).show();
|
).show();
|
||||||
} else if(error instanceof KeyServer.InsufficientQuery){
|
} else if(error instanceof KeyServer.InsufficientQuery) {
|
||||||
AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query,
|
AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query,
|
||||||
AppMsg.STYLE_ALERT).show();
|
AppMsg.STYLE_ALERT).show();
|
||||||
}else if(error instanceof KeyServer.QueryException){
|
} else if(error instanceof KeyServer.QueryException) {
|
||||||
AppMsg.makeText(getActivity(), R.string.error_keyserver_query,
|
AppMsg.makeText(getActivity(), R.string.error_keyserver_query,
|
||||||
AppMsg.STYLE_ALERT).show();
|
AppMsg.STYLE_ALERT).show();
|
||||||
}else if(error instanceof KeyServer.TooManyResponses){
|
} else if(error instanceof KeyServer.TooManyResponses) {
|
||||||
AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses,
|
AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses,
|
||||||
AppMsg.STYLE_ALERT).show();
|
AppMsg.STYLE_ALERT).show();
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,8 @@ public class KeyListActivity extends DrawerActivity {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_key_list_export:
|
case R.id.menu_key_list_export:
|
||||||
mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR
|
// TODO fix this for unified keylist
|
||||||
+ "/pubexport.asc");
|
mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
|
@ -19,11 +19,14 @@ package org.sufficientlysecure.keychain.ui;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
|
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes;
|
||||||
@ -53,13 +56,21 @@ import android.support.v4.app.LoaderManager;
|
|||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
import android.support.v4.widget.CursorAdapter;
|
import android.support.v4.widget.CursorAdapter;
|
||||||
|
import android.support.v4.view.MenuItemCompat;
|
||||||
|
import android.support.v7.app.ActionBarActivity;
|
||||||
|
import android.support.v7.widget.SearchView;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.AnimationUtils;
|
||||||
import android.widget.AbsListView.MultiChoiceModeListener;
|
import android.widget.AbsListView.MultiChoiceModeListener;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@ -73,24 +84,37 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
|
|||||||
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
||||||
* StickyListHeaders library which does not extend upon ListView.
|
* StickyListHeaders library which does not extend upon ListView.
|
||||||
*/
|
*/
|
||||||
public class KeyListFragment extends Fragment implements AdapterView.OnItemClickListener,
|
public class KeyListFragment extends Fragment implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
||||||
LoaderManager.LoaderCallbacks<Cursor> {
|
LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
private KeyListAdapter mAdapter;
|
private KeyListAdapter mAdapter;
|
||||||
private StickyListHeadersListView mStickyList;
|
private StickyListHeadersListView mStickyList;
|
||||||
|
|
||||||
|
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||||
|
boolean mListShown;
|
||||||
|
View mProgressContainer;
|
||||||
|
View mListContainer;
|
||||||
|
|
||||||
|
private String mCurQuery;
|
||||||
|
private SearchView mSearchView;
|
||||||
// empty list layout
|
// empty list layout
|
||||||
private BootstrapButton mButtonEmptyCreate;
|
private BootstrapButton mButtonEmptyCreate;
|
||||||
private BootstrapButton mButtonEmptyImport;
|
private BootstrapButton mButtonEmptyImport;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load custom layout with StickyListView from library
|
* Load custom layout with StickyListView from library
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.key_list_fragment, container, false);
|
View root = inflater.inflate(R.layout.key_list_fragment, container, false);
|
||||||
|
|
||||||
mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create);
|
mStickyList = (StickyListHeadersListView) root.findViewById(R.id.key_list_list);
|
||||||
|
mStickyList.setOnItemClickListener(this);
|
||||||
|
|
||||||
|
|
||||||
|
// empty view
|
||||||
|
mButtonEmptyCreate = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_create);
|
||||||
mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
|
mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -102,8 +126,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
startActivityForResult(intent, 0);
|
startActivityForResult(intent, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
mButtonEmptyImport = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_import);
|
||||||
mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import);
|
|
||||||
mButtonEmptyImport.setOnClickListener(new OnClickListener() {
|
mButtonEmptyImport.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -114,7 +137,12 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return view;
|
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||||
|
mListContainer = root.findViewById(R.id.key_list_list_container);
|
||||||
|
mProgressContainer = root.findViewById(R.id.key_list_progress_container);
|
||||||
|
mListShown = true;
|
||||||
|
|
||||||
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,8 +153,6 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
|
|
||||||
|
|
||||||
mStickyList.setOnItemClickListener(this);
|
mStickyList.setOnItemClickListener(this);
|
||||||
mStickyList.setAreHeadersSticky(true);
|
mStickyList.setAreHeadersSticky(true);
|
||||||
mStickyList.setDrawingListUnderStickyHeader(false);
|
mStickyList.setDrawingListUnderStickyHeader(false);
|
||||||
@ -137,7 +163,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this view is made visible if no data is available
|
// this view is made visible if no data is available
|
||||||
mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
|
mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
|
* ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
|
||||||
@ -147,8 +173,6 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||||
mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
|
mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
|
||||||
|
|
||||||
private int count = 0;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||||
android.view.MenuInflater inflater = getActivity().getMenuInflater();
|
android.view.MenuInflater inflater = getActivity().getMenuInflater();
|
||||||
@ -174,7 +198,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.menu_key_list_multi_delete: {
|
case R.id.menu_key_list_multi_delete: {
|
||||||
ids = mAdapter.getCurrentSelectedItemIds();
|
ids = mStickyList.getWrappedList().getCheckedItemIds();
|
||||||
showDeleteKeyDialog(mode, ids);
|
showDeleteKeyDialog(mode, ids);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -184,7 +208,6 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyActionMode(ActionMode mode) {
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
count = 0;
|
|
||||||
mAdapter.clearSelection();
|
mAdapter.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,13 +215,11 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
|
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
|
||||||
boolean checked) {
|
boolean checked) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
count++;
|
|
||||||
mAdapter.setNewSelection(position, checked);
|
mAdapter.setNewSelection(position, checked);
|
||||||
} else {
|
} else {
|
||||||
count--;
|
|
||||||
mAdapter.removeSelection(position);
|
mAdapter.removeSelection(position);
|
||||||
}
|
}
|
||||||
|
int count = mStickyList.getCheckedItemCount();
|
||||||
String keysSelected = getResources().getQuantityString(
|
String keysSelected = getResources().getQuantityString(
|
||||||
R.plurals.key_list_selected_keys, count, count);
|
R.plurals.key_list_selected_keys, count, count);
|
||||||
mode.setTitle(keysSelected);
|
mode.setTitle(keysSelected);
|
||||||
@ -207,9 +228,12 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
|
// We have a menu item to show in action bar.
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
// NOTE: Not supported by StickyListHeader, but reimplemented here
|
||||||
// Start out with a progress indicator.
|
// Start out with a progress indicator.
|
||||||
// setListShown(false);
|
setListShown(false);
|
||||||
|
|
||||||
// Create an empty adapter we will use to display the loaded data.
|
// Create an empty adapter we will use to display the loaded data.
|
||||||
mAdapter = new KeyListAdapter(getActivity(), null, Id.type.public_key);
|
mAdapter = new KeyListAdapter(getActivity(), null, Id.type.public_key);
|
||||||
@ -238,27 +262,33 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
// This is called when a new Loader needs to be created. This
|
// This is called when a new Loader needs to be created. This
|
||||||
// sample only has one Loader, so we don't care about the ID.
|
// sample only has one Loader, so we don't care about the ID.
|
||||||
Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
|
Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
|
||||||
|
String where = null;
|
||||||
|
String whereArgs[] = null;
|
||||||
|
if (mCurQuery != null) {
|
||||||
|
where = KeychainContract.UserIds.USER_ID + " LIKE ?";
|
||||||
|
whereArgs = new String[]{"%" + mCurQuery + "%"};
|
||||||
|
}
|
||||||
// Now create and return a CursorLoader that will take care of
|
// Now create and return a CursorLoader that will take care of
|
||||||
// creating a Cursor for the data being displayed.
|
// creating a Cursor for the data being displayed.
|
||||||
return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER);
|
return new CursorLoader(getActivity(), baseUri, PROJECTION, where, whereArgs, SORT_ORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||||
// Swap the new cursor in. (The framework will take care of closing the
|
// Swap the new cursor in. (The framework will take care of closing the
|
||||||
// old cursor once we return.)
|
// old cursor once we return.)
|
||||||
|
mAdapter.setSearchQuery(mCurQuery);
|
||||||
mAdapter.swapCursor(data);
|
mAdapter.swapCursor(data);
|
||||||
|
|
||||||
mStickyList.setAdapter(mAdapter);
|
mStickyList.setAdapter(mAdapter);
|
||||||
|
|
||||||
// NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
|
// NOTE: Not supported by StickyListHeader, but reimplemented here
|
||||||
// The list should now be shown.
|
// The list should now be shown.
|
||||||
// if (isResumed()) {
|
if (isResumed()) {
|
||||||
// setListShown(true);
|
setListShown(true);
|
||||||
// } else {
|
} else {
|
||||||
// setListShownNoAnimation(true);
|
setListShownNoAnimation(true);
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -338,6 +368,87 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
|
deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||||
|
// Get the searchview
|
||||||
|
MenuItem searchItem = menu.findItem(R.id.menu_key_list_search);
|
||||||
|
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||||
|
|
||||||
|
// Execute this when searching
|
||||||
|
mSearchView.setOnQueryTextListener(this);
|
||||||
|
|
||||||
|
// Erase search result without focus
|
||||||
|
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||||
|
mCurQuery = null;
|
||||||
|
mSearchView.setQuery("", true);
|
||||||
|
getLoaderManager().restartLoader(0, null, KeyListFragment.this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String s) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String s) {
|
||||||
|
// Called when the action bar search text has changed. Update
|
||||||
|
// the search filter, and restart the loader to do a new query
|
||||||
|
// with this filter.
|
||||||
|
mCurQuery = !TextUtils.isEmpty(s) ? s : null;
|
||||||
|
getLoaderManager().restartLoader(0, null, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||||
|
public void setListShown(boolean shown, boolean animate) {
|
||||||
|
if (mListShown == shown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mListShown = shown;
|
||||||
|
if (shown) {
|
||||||
|
if (animate) {
|
||||||
|
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||||
|
getActivity(), android.R.anim.fade_out));
|
||||||
|
mListContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||||
|
getActivity(), android.R.anim.fade_in));
|
||||||
|
}
|
||||||
|
mProgressContainer.setVisibility(View.GONE);
|
||||||
|
mListContainer.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
if (animate) {
|
||||||
|
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||||
|
getActivity(), android.R.anim.fade_in));
|
||||||
|
mListContainer.startAnimation(AnimationUtils.loadAnimation(
|
||||||
|
getActivity(), android.R.anim.fade_out));
|
||||||
|
}
|
||||||
|
mProgressContainer.setVisibility(View.VISIBLE);
|
||||||
|
mListContainer.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||||
|
public void setListShown(boolean shown) {
|
||||||
|
setListShown(shown, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
|
||||||
|
public void setListShownNoAnimation(boolean shown) {
|
||||||
|
setListShown(shown, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements StickyListHeadersAdapter from library
|
* Implements StickyListHeadersAdapter from library
|
||||||
*/
|
*/
|
||||||
@ -347,6 +458,8 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
private int mIndexIsRevoked;
|
private int mIndexIsRevoked;
|
||||||
private int mMasterKeyId;
|
private int mMasterKeyId;
|
||||||
|
|
||||||
|
private String mCurQuery;
|
||||||
|
|
||||||
@SuppressLint("UseSparseArrays")
|
@SuppressLint("UseSparseArrays")
|
||||||
private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
|
private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
|
||||||
|
|
||||||
@ -394,12 +507,12 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
String userId = cursor.getString(mIndexUserId);
|
String userId = cursor.getString(mIndexUserId);
|
||||||
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
||||||
if (userIdSplit[0] != null) {
|
if (userIdSplit[0] != null) {
|
||||||
mainUserId.setText(userIdSplit[0]);
|
mainUserId.setText(highlightSearchQuery(userIdSplit[0]));
|
||||||
} else {
|
} else {
|
||||||
mainUserId.setText(R.string.user_id_no_name);
|
mainUserId.setText(R.string.user_id_no_name);
|
||||||
}
|
}
|
||||||
if (userIdSplit[1] != null) {
|
if (userIdSplit[1] != null) {
|
||||||
mainUserIdRest.setText(userIdSplit[1]);
|
mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1]));
|
||||||
mainUserIdRest.setVisibility(View.VISIBLE);
|
mainUserIdRest.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
mainUserIdRest.setVisibility(View.GONE);
|
mainUserIdRest.setVisibility(View.GONE);
|
||||||
@ -558,20 +671,6 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPositionChecked(int position) {
|
|
||||||
Boolean result = mSelection.get(position);
|
|
||||||
return result == null ? false : result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getCurrentSelectedItemIds() {
|
|
||||||
long[] ids = new long[mSelection.size()];
|
|
||||||
int i = 0;
|
|
||||||
// get master key ids
|
|
||||||
for (int pos : mSelection.keySet())
|
|
||||||
ids[i++] = mAdapter.getItemId(pos);
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getCurrentSelectedMasterKeyIds() {
|
public long[] getCurrentSelectedMasterKeyIds() {
|
||||||
long[] ids = new long[mSelection.size()];
|
long[] ids = new long[mSelection.size()];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -608,6 +707,34 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// search highlight methods
|
||||||
|
|
||||||
|
public void setSearchQuery(String searchQuery) {
|
||||||
|
mCurQuery = searchQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSearchQuery() {
|
||||||
|
return mCurQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Spannable highlightSearchQuery(String text) {
|
||||||
|
Spannable highlight = Spannable.Factory.getInstance().newSpannable(text);
|
||||||
|
|
||||||
|
if (mCurQuery != null) {
|
||||||
|
Pattern pattern = Pattern.compile("(?i)" + mCurQuery);
|
||||||
|
Matcher matcher = pattern.matcher(text);
|
||||||
|
if (matcher.find()) {
|
||||||
|
highlight.setSpan(
|
||||||
|
new ForegroundColorSpan(mContext.getResources().getColor(R.color.emphasis)),
|
||||||
|
matcher.start(),
|
||||||
|
matcher.end(),
|
||||||
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
return highlight;
|
||||||
|
} else {
|
||||||
|
return highlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,24 +24,27 @@ import org.sufficientlysecure.keychain.R;
|
|||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||||
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
|
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.CheckBoxPreference;
|
import android.preference.CheckBoxPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceActivity;
|
import android.preference.PreferenceActivity;
|
||||||
|
import android.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
public class PreferencesActivity extends PreferenceActivity {
|
public class PreferencesActivity extends PreferenceActivity {
|
||||||
private IntegerListPreference mPassPhraseCacheTtl = null;
|
|
||||||
private IntegerListPreference mEncryptionAlgorithm = null;
|
public final static String ACTION_PREFS_GEN = "org.sufficientlysecure.keychain.ui.PREFS_GEN";
|
||||||
private IntegerListPreference mHashAlgorithm = null;
|
public final static String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
|
||||||
private IntegerListPreference mMessageCompression = null;
|
|
||||||
private IntegerListPreference mFileCompression = null;
|
|
||||||
private CheckBoxPreference mAsciiArmour = null;
|
|
||||||
private CheckBoxPreference mForceV3Signatures = null;
|
|
||||||
private PreferenceScreen mKeyServerPreference = null;
|
private PreferenceScreen mKeyServerPreference = null;
|
||||||
private Preferences mPreferences;
|
private static Preferences mPreferences;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -53,22 +56,218 @@ public class PreferencesActivity extends PreferenceActivity {
|
|||||||
// actionBar.setDisplayHomeAsUpEnabled(false);
|
// actionBar.setDisplayHomeAsUpEnabled(false);
|
||||||
// actionBar.setHomeButtonEnabled(false);
|
// actionBar.setHomeButtonEnabled(false);
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.preferences);
|
String action = getIntent().getAction();
|
||||||
|
|
||||||
mPassPhraseCacheTtl = (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL);
|
if (action != null && action.equals(ACTION_PREFS_GEN)) {
|
||||||
mPassPhraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl());
|
addPreferencesFromResource(R.xml.gen_preferences);
|
||||||
mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
|
|
||||||
mPassPhraseCacheTtl
|
initializePassPassPhraceCacheTtl(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL));
|
||||||
|
|
||||||
|
mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS);
|
||||||
|
String servers[] = mPreferences.getKeyServers();
|
||||||
|
mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers,
|
||||||
|
servers.length, servers.length));
|
||||||
|
mKeyServerPreference
|
||||||
|
.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
Intent intent = new Intent(PreferencesActivity.this,
|
||||||
|
PreferencesKeyServerActivity.class);
|
||||||
|
intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS,
|
||||||
|
mPreferences.getKeyServers());
|
||||||
|
startActivityForResult(intent, Id.request.key_server_preference);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (action != null && action.equals(ACTION_PREFS_ADV)) {
|
||||||
|
addPreferencesFromResource(R.xml.adv_preferences);
|
||||||
|
|
||||||
|
initializeEncryptionAlgorithm(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM));
|
||||||
|
|
||||||
|
int[] valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip,
|
||||||
|
Id.choice.compression.zlib, Id.choice.compression.bzip2, };
|
||||||
|
String[] entries = new String[] {
|
||||||
|
getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
|
||||||
|
"ZIP (" + getString(R.string.compression_fast) + ")",
|
||||||
|
"ZLIB (" + getString(R.string.compression_fast) + ")",
|
||||||
|
"BZIP2 (" + getString(R.string.compression_very_slow) + ")", };
|
||||||
|
String[] values = new String[valueIds.length];
|
||||||
|
for (int i = 0; i < values.length; ++i) {
|
||||||
|
values[i] = "" + valueIds[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeHashAlgorithm(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM),
|
||||||
|
valueIds, entries, values);
|
||||||
|
|
||||||
|
initializeMessageCompression(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION),
|
||||||
|
valueIds, entries, values);
|
||||||
|
|
||||||
|
initializeFileCompression(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION),
|
||||||
|
entries, values);
|
||||||
|
|
||||||
|
initializeAsciiArmour((CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR));
|
||||||
|
|
||||||
|
initializeForceV3Signatures((CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES));
|
||||||
|
|
||||||
|
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
// Load the legacy preferences headers
|
||||||
|
addPreferencesFromResource(R.xml.preference_headers_legacy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
switch (requestCode) {
|
||||||
|
case Id.request.key_server_preference: {
|
||||||
|
if (resultCode == RESULT_CANCELED || data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String servers[] = data
|
||||||
|
.getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS);
|
||||||
|
mPreferences.setKeyServers(servers);
|
||||||
|
mKeyServerPreference.setSummary(getResources().getQuantityString(
|
||||||
|
R.plurals.n_key_servers, servers.length, servers.length));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called only on Honeycomb and later */
|
||||||
|
@Override
|
||||||
|
public void onBuildHeaders(List<Header> target) {
|
||||||
|
super.onBuildHeaders(target);
|
||||||
|
loadHeadersFromResource(R.xml.preference_headers, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This fragment shows the general preferences in android 3.0+ */
|
||||||
|
public static class GeneralPrefsFragment extends PreferenceFragment {
|
||||||
|
|
||||||
|
private PreferenceScreen mKeyServerPreference = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Load the preferences from an XML resource
|
||||||
|
addPreferencesFromResource(R.xml.gen_preferences);
|
||||||
|
|
||||||
|
initializePassPassPhraceCacheTtl(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL));
|
||||||
|
|
||||||
|
mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS);
|
||||||
|
String servers[] = mPreferences.getKeyServers();
|
||||||
|
mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers,
|
||||||
|
servers.length, servers.length));
|
||||||
|
mKeyServerPreference
|
||||||
|
.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
Intent intent = new Intent(getActivity(),
|
||||||
|
PreferencesKeyServerActivity.class);
|
||||||
|
intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS,
|
||||||
|
mPreferences.getKeyServers());
|
||||||
|
startActivityForResult(intent, Id.request.key_server_preference);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
switch (requestCode) {
|
||||||
|
case Id.request.key_server_preference: {
|
||||||
|
if (resultCode == RESULT_CANCELED || data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String servers[] = data
|
||||||
|
.getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS);
|
||||||
|
mPreferences.setKeyServers(servers);
|
||||||
|
mKeyServerPreference.setSummary(getResources().getQuantityString(
|
||||||
|
R.plurals.n_key_servers, servers.length, servers.length));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This fragment shows the advanced preferences in android 3.0+ */
|
||||||
|
public static class AdvancedPrefsFragment extends PreferenceFragment {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Load the preferences from an XML resource
|
||||||
|
addPreferencesFromResource(R.xml.adv_preferences);
|
||||||
|
|
||||||
|
initializeEncryptionAlgorithm(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM));
|
||||||
|
|
||||||
|
int[] valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip,
|
||||||
|
Id.choice.compression.zlib, Id.choice.compression.bzip2, };
|
||||||
|
String[] entries = new String[] {
|
||||||
|
getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
|
||||||
|
"ZIP (" + getString(R.string.compression_fast) + ")",
|
||||||
|
"ZLIB (" + getString(R.string.compression_fast) + ")",
|
||||||
|
"BZIP2 (" + getString(R.string.compression_very_slow) + ")", };
|
||||||
|
String[] values = new String[valueIds.length];
|
||||||
|
for (int i = 0; i < values.length; ++i) {
|
||||||
|
values[i] = "" + valueIds[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeHashAlgorithm(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM),
|
||||||
|
valueIds, entries, values);
|
||||||
|
|
||||||
|
initializeMessageCompression(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION),
|
||||||
|
valueIds, entries, values);
|
||||||
|
|
||||||
|
initializeFileCompression(
|
||||||
|
(IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION),
|
||||||
|
entries, values);
|
||||||
|
|
||||||
|
initializeAsciiArmour((CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR));
|
||||||
|
|
||||||
|
initializeForceV3Signatures((CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isValidFragment (String fragmentName) {
|
||||||
|
return AdvancedPrefsFragment.class.getName().equals(fragmentName)
|
||||||
|
|| GeneralPrefsFragment.class.getName().equals(fragmentName)
|
||||||
|
|| super.isValidFragment(fragmentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initializePassPassPhraceCacheTtl(final IntegerListPreference mPassphraseCacheTtl) {
|
||||||
|
mPassphraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl());
|
||||||
|
mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
|
||||||
|
mPassphraseCacheTtl
|
||||||
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
mPassPhraseCacheTtl.setValue(newValue.toString());
|
mPassphraseCacheTtl.setValue(newValue.toString());
|
||||||
mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
|
mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
|
||||||
mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
|
mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mEncryptionAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM);
|
private static void initializeEncryptionAlgorithm(final IntegerListPreference mEncryptionAlgorithm) {
|
||||||
int valueIds[] = { PGPEncryptedData.AES_128, PGPEncryptedData.AES_192,
|
int valueIds[] = { PGPEncryptedData.AES_128, PGPEncryptedData.AES_192,
|
||||||
PGPEncryptedData.AES_256, PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH,
|
PGPEncryptedData.AES_256, PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH,
|
||||||
PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES,
|
PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES,
|
||||||
@ -93,8 +292,10 @@ public class PreferencesActivity extends PreferenceActivity {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mHashAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM);
|
private static void initializeHashAlgorithm
|
||||||
|
(final IntegerListPreference mHashAlgorithm, int[] valueIds, String[] entries, String[] values) {
|
||||||
valueIds = new int[] { HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160,
|
valueIds = new int[] { HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160,
|
||||||
HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256,
|
HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256,
|
||||||
HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512, };
|
HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512, };
|
||||||
@ -116,19 +317,10 @@ public class PreferencesActivity extends PreferenceActivity {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mMessageCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION);
|
private static void initializeMessageCompression
|
||||||
valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip,
|
(final IntegerListPreference mMessageCompression, int[] valueIds, String[] entries, String[] values) {
|
||||||
Id.choice.compression.zlib, Id.choice.compression.bzip2, };
|
|
||||||
entries = new String[] {
|
|
||||||
getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
|
|
||||||
"ZIP (" + getString(R.string.compression_fast) + ")",
|
|
||||||
"ZLIB (" + getString(R.string.compression_fast) + ")",
|
|
||||||
"BZIP2 (" + getString(R.string.compression_very_slow) + ")", };
|
|
||||||
values = new String[valueIds.length];
|
|
||||||
for (int i = 0; i < values.length; ++i) {
|
|
||||||
values[i] = "" + valueIds[i];
|
|
||||||
}
|
|
||||||
mMessageCompression.setEntries(entries);
|
mMessageCompression.setEntries(entries);
|
||||||
mMessageCompression.setEntryValues(values);
|
mMessageCompression.setEntryValues(values);
|
||||||
mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
|
mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
|
||||||
@ -143,8 +335,10 @@ public class PreferencesActivity extends PreferenceActivity {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mFileCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION);
|
private static void initializeFileCompression
|
||||||
|
(final IntegerListPreference mFileCompression, String[] entries, String[] values) {
|
||||||
mFileCompression.setEntries(entries);
|
mFileCompression.setEntries(entries);
|
||||||
mFileCompression.setEntryValues(values);
|
mFileCompression.setEntryValues(values);
|
||||||
mFileCompression.setValue("" + mPreferences.getDefaultFileCompression());
|
mFileCompression.setValue("" + mPreferences.getDefaultFileCompression());
|
||||||
@ -157,8 +351,9 @@ public class PreferencesActivity extends PreferenceActivity {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mAsciiArmour = (CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR);
|
private static void initializeAsciiArmour(final CheckBoxPreference mAsciiArmour) {
|
||||||
mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour());
|
mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour());
|
||||||
mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
@ -167,8 +362,9 @@ public class PreferencesActivity extends PreferenceActivity {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mForceV3Signatures = (CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES);
|
private static void initializeForceV3Signatures(final CheckBoxPreference mForceV3Signatures) {
|
||||||
mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures());
|
mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures());
|
||||||
mForceV3Signatures
|
mForceV3Signatures
|
||||||
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
@ -178,43 +374,5 @@ public class PreferencesActivity extends PreferenceActivity {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS);
|
|
||||||
String servers[] = mPreferences.getKeyServers();
|
|
||||||
mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers,
|
|
||||||
servers.length, servers.length));
|
|
||||||
mKeyServerPreference
|
|
||||||
.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
Intent intent = new Intent(PreferencesActivity.this,
|
|
||||||
PreferencesKeyServerActivity.class);
|
|
||||||
intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS,
|
|
||||||
mPreferences.getKeyServers());
|
|
||||||
startActivityForResult(intent, Id.request.key_server_preference);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@Override
|
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
||||||
switch (requestCode) {
|
|
||||||
case Id.request.key_server_preference: {
|
|
||||||
if (resultCode == RESULT_CANCELED || data == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String servers[] = data
|
|
||||||
.getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS);
|
|
||||||
mPreferences.setKeyServers(servers);
|
|
||||||
mKeyServerPreference.setSummary(getResources().getQuantityString(
|
|
||||||
R.plurals.n_key_servers, servers.length, servers.length));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -50,14 +50,14 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Inflate a "Done"/"Cancel" custom action bar view
|
// Inflate a "Done"/"Cancel" custom action bar view
|
||||||
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
|
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// ok
|
// ok
|
||||||
okClicked();
|
okClicked();
|
||||||
}
|
}
|
||||||
}, R.string.btn_do_not_save, new View.OnClickListener() {
|
}, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// cancel
|
// cancel
|
||||||
@ -81,11 +81,11 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
|
|||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS);
|
String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS);
|
||||||
if (servers != null) {
|
if (servers != null) {
|
||||||
for (int i = 0; i < servers.length; ++i) {
|
for (String serv : servers) {
|
||||||
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(
|
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(
|
||||||
R.layout.key_server_editor, mEditors, false);
|
R.layout.key_server_editor, mEditors, false);
|
||||||
view.setEditorListener(this);
|
view.setEditorListener(this);
|
||||||
view.setValue(servers[i]);
|
view.setValue(serv);
|
||||||
mEditors.addView(view);
|
mEditors.addView(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,14 +46,14 @@ public class SelectPublicKeyActivity extends ActionBarActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Inflate a "Done"/"Cancel" custom action bar view
|
// Inflate a "Done"/"Cancel" custom action bar view
|
||||||
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
|
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_okay, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// ok
|
// ok
|
||||||
okClicked();
|
okClicked();
|
||||||
}
|
}
|
||||||
}, R.string.btn_do_not_save, new View.OnClickListener() {
|
}, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// cancel
|
// cancel
|
||||||
|
@ -30,7 +30,7 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase;
|
|||||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
|
import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.DatabaseUtils;
|
import android.database.DatabaseUtils;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -38,17 +38,35 @@ import android.os.Bundle;
|
|||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
public class SelectPublicKeyFragment extends ListFragmentWorkaround implements TextWatcher,
|
||||||
LoaderManager.LoaderCallbacks<Cursor> {
|
LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
public static final String ARG_PRESELECTED_KEY_IDS = "preselected_key_ids";
|
public static final String ARG_PRESELECTED_KEY_IDS = "preselected_key_ids";
|
||||||
|
|
||||||
private Activity mActivity;
|
|
||||||
private SelectKeyCursorAdapter mAdapter;
|
private SelectKeyCursorAdapter mAdapter;
|
||||||
private ListView mListView;
|
private EditText mSearchView;
|
||||||
|
|
||||||
private long mSelectedMasterKeyIds[];
|
private long mSelectedMasterKeyIds[];
|
||||||
|
private String mCurQuery;
|
||||||
|
|
||||||
|
// copied from ListFragment
|
||||||
|
static final int INTERNAL_EMPTY_ID = 0x00ff0001;
|
||||||
|
static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002;
|
||||||
|
static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003;
|
||||||
|
// added for search view
|
||||||
|
static final int SEARCH_ID = 0x00ff0004;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of this fragment
|
* Creates new instance of this fragment
|
||||||
@ -67,10 +85,84 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
mSelectedMasterKeyIds = getArguments().getLongArray(ARG_PRESELECTED_KEY_IDS);
|
mSelectedMasterKeyIds = getArguments().getLongArray(ARG_PRESELECTED_KEY_IDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copied from ListFragment and added EditText for search on top of list.
|
||||||
|
* We do not use a custom layout here, because this breaks the progress bar functionality
|
||||||
|
* of ListFragment.
|
||||||
|
*
|
||||||
|
* @param inflater
|
||||||
|
* @param container
|
||||||
|
* @param savedInstanceState
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
final Context context = getActivity();
|
||||||
|
|
||||||
|
FrameLayout root = new FrameLayout(context);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
LinearLayout pframe = new LinearLayout(context);
|
||||||
|
pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID);
|
||||||
|
pframe.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
pframe.setVisibility(View.GONE);
|
||||||
|
pframe.setGravity(Gravity.CENTER);
|
||||||
|
|
||||||
|
ProgressBar progress = new ProgressBar(context, null,
|
||||||
|
android.R.attr.progressBarStyleLarge);
|
||||||
|
pframe.addView(progress, new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
root.addView(pframe, new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
FrameLayout lframe = new FrameLayout(context);
|
||||||
|
lframe.setId(INTERNAL_LIST_CONTAINER_ID);
|
||||||
|
|
||||||
|
TextView tv = new TextView(getActivity());
|
||||||
|
tv.setId(INTERNAL_EMPTY_ID);
|
||||||
|
tv.setGravity(Gravity.CENTER);
|
||||||
|
lframe.addView(tv, new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
|
||||||
|
|
||||||
|
// Added for search view: linearLayout, mSearchView
|
||||||
|
LinearLayout linearLayout = new LinearLayout(context);
|
||||||
|
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
mSearchView = new EditText(context);
|
||||||
|
mSearchView.setId(SEARCH_ID);
|
||||||
|
mSearchView.setHint(R.string.menu_search);
|
||||||
|
mSearchView.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_action_search), null, null, null);
|
||||||
|
|
||||||
|
linearLayout.addView(mSearchView, new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
ListView lv = new ListView(getActivity());
|
||||||
|
lv.setId(android.R.id.list);
|
||||||
|
lv.setDrawSelectorOnTop(false);
|
||||||
|
linearLayout.addView(lv, new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
|
||||||
|
|
||||||
|
lframe.addView(linearLayout, new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
|
||||||
|
|
||||||
|
root.addView(lframe, new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
root.setLayoutParams(new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define Adapter and Loader on create of Activity
|
* Define Adapter and Loader on create of Activity
|
||||||
*/
|
*/
|
||||||
@ -78,16 +170,15 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
|||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
mActivity = getActivity();
|
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||||
mListView = getListView();
|
|
||||||
|
|
||||||
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
|
||||||
|
|
||||||
// Give some text to display if there is no data. In a real
|
// Give some text to display if there is no data. In a real
|
||||||
// application this would come from a resource.
|
// application this would come from a resource.
|
||||||
setEmptyText(getString(R.string.list_empty));
|
setEmptyText(getString(R.string.list_empty));
|
||||||
|
|
||||||
mAdapter = new SelectKeyCursorAdapter(mActivity, null, 0, mListView, Id.type.public_key);
|
mSearchView.addTextChangedListener(this);
|
||||||
|
|
||||||
|
mAdapter = new SelectKeyCursorAdapter(getActivity(), null, 0, getListView(), Id.type.public_key);
|
||||||
|
|
||||||
setListAdapter(mAdapter);
|
setListAdapter(mAdapter);
|
||||||
|
|
||||||
@ -101,16 +192,16 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects items based on master key ids in list view
|
* Selects items based on master key ids in list view
|
||||||
*
|
*
|
||||||
* @param masterKeyIds
|
* @param masterKeyIds
|
||||||
*/
|
*/
|
||||||
private void preselectMasterKeyIds(long[] masterKeyIds) {
|
private void preselectMasterKeyIds(long[] masterKeyIds) {
|
||||||
if (masterKeyIds != null) {
|
if (masterKeyIds != null) {
|
||||||
for (int i = 0; i < mListView.getCount(); ++i) {
|
for (int i = 0; i < getListView().getCount(); ++i) {
|
||||||
long keyId = mAdapter.getMasterKeyId(i);
|
long keyId = mAdapter.getMasterKeyId(i);
|
||||||
for (int j = 0; j < masterKeyIds.length; ++j) {
|
for (long masterKeyId : masterKeyIds) {
|
||||||
if (keyId == masterKeyIds[j]) {
|
if (keyId == masterKeyId) {
|
||||||
mListView.setItemChecked(i, true);
|
getListView().setItemChecked(i, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,15 +211,15 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all selected master key ids
|
* Returns all selected master key ids
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public long[] getSelectedMasterKeyIds() {
|
public long[] getSelectedMasterKeyIds() {
|
||||||
// mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key
|
// mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key
|
||||||
// ids!
|
// ids!
|
||||||
Vector<Long> vector = new Vector<Long>();
|
Vector<Long> vector = new Vector<Long>();
|
||||||
for (int i = 0; i < mListView.getCount(); ++i) {
|
for (int i = 0; i < getListView().getCount(); ++i) {
|
||||||
if (mListView.isItemChecked(i)) {
|
if (getListView().isItemChecked(i)) {
|
||||||
vector.add(mAdapter.getMasterKeyId(i));
|
vector.add(mAdapter.getMasterKeyId(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,13 +235,13 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all selected user ids
|
* Returns all selected user ids
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String[] getSelectedUserIds() {
|
public String[] getSelectedUserIds() {
|
||||||
Vector<String> userIds = new Vector<String>();
|
Vector<String> userIds = new Vector<String>();
|
||||||
for (int i = 0; i < mListView.getCount(); ++i) {
|
for (int i = 0; i < getListView().getCount(); ++i) {
|
||||||
if (mListView.isItemChecked(i)) {
|
if (getListView().isItemChecked(i)) {
|
||||||
userIds.add((String) mAdapter.getUserId(i));
|
userIds.add((String) mAdapter.getUserId(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +259,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
|||||||
|
|
||||||
// These are the rows that we will retrieve.
|
// These are the rows that we will retrieve.
|
||||||
long now = new Date().getTime() / 1000;
|
long now = new Date().getTime() / 1000;
|
||||||
String[] projection = new String[] {
|
String[] projection = new String[]{
|
||||||
KeyRings._ID,
|
KeyRings._ID,
|
||||||
KeyRings.MASTER_KEY_ID,
|
KeyRings.MASTER_KEY_ID,
|
||||||
UserIds.USER_ID,
|
UserIds.USER_ID,
|
||||||
@ -185,7 +276,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
|||||||
+ Keys.CAN_ENCRYPT + " = '1' AND valid_keys." + Keys.CREATION + " <= '"
|
+ Keys.CAN_ENCRYPT + " = '1' AND valid_keys." + Keys.CREATION + " <= '"
|
||||||
+ now + "' AND " + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys."
|
+ now + "' AND " + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys."
|
||||||
+ Keys.EXPIRY + " >= '" + now + "')) AS "
|
+ Keys.EXPIRY + " >= '" + now + "')) AS "
|
||||||
+ SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
|
+ SelectKeyCursorAdapter.PROJECTION_ROW_VALID,};
|
||||||
|
|
||||||
String inMasterKeyList = null;
|
String inMasterKeyList = null;
|
||||||
if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) {
|
if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) {
|
||||||
@ -199,37 +290,28 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
|||||||
inMasterKeyList += ")";
|
inMasterKeyList += ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (searchString != null && searchString.trim().length() > 0) {
|
|
||||||
// String[] chunks = searchString.trim().split(" +");
|
|
||||||
// qb.appendWhere("(EXISTS (SELECT tmp." + UserIds._ID + " FROM " + UserIds.TABLE_NAME
|
|
||||||
// + " AS tmp WHERE " + "tmp." + UserIds.KEY_ID + " = " + Keys.TABLE_NAME + "."
|
|
||||||
// + Keys._ID);
|
|
||||||
// for (int i = 0; i < chunks.length; ++i) {
|
|
||||||
// qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
|
|
||||||
// qb.appendWhereEscapeString("%" + chunks[i] + "%");
|
|
||||||
// }
|
|
||||||
// qb.appendWhere("))");
|
|
||||||
//
|
|
||||||
// if (inIdList != null) {
|
|
||||||
// qb.appendWhere(" OR (" + inIdList + ")");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
String orderBy = UserIds.USER_ID + " ASC";
|
String orderBy = UserIds.USER_ID + " ASC";
|
||||||
if (inMasterKeyList != null) {
|
if (inMasterKeyList != null) {
|
||||||
// sort by selected master keys
|
// sort by selected master keys
|
||||||
orderBy = inMasterKeyList + " DESC, " + orderBy;
|
orderBy = inMasterKeyList + " DESC, " + orderBy;
|
||||||
}
|
}
|
||||||
|
String where = null;
|
||||||
|
String whereArgs[] = null;
|
||||||
|
if (mCurQuery != null) {
|
||||||
|
where = UserIds.USER_ID + " LIKE ?";
|
||||||
|
whereArgs = new String[]{"%" + mCurQuery + "%"};
|
||||||
|
}
|
||||||
|
|
||||||
// Now create and return a CursorLoader that will take care of
|
// Now create and return a CursorLoader that will take care of
|
||||||
// creating a Cursor for the data being displayed.
|
// creating a Cursor for the data being displayed.
|
||||||
return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy);
|
return new CursorLoader(getActivity(), baseUri, projection, where, whereArgs, orderBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||||
// Swap the new cursor in. (The framework will take care of closing the
|
// Swap the new cursor in. (The framework will take care of closing the
|
||||||
// old cursor once we return.)
|
// old cursor once we return.)
|
||||||
|
mAdapter.setSearchQuery(mCurQuery);
|
||||||
mAdapter.swapCursor(data);
|
mAdapter.swapCursor(data);
|
||||||
|
|
||||||
// The list should now be shown.
|
// The list should now be shown.
|
||||||
@ -250,4 +332,20 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
|||||||
// longer using it.
|
// longer using it.
|
||||||
mAdapter.swapCursor(null);
|
mAdapter.swapCursor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable editable) {
|
||||||
|
mCurQuery = !TextUtils.isEmpty(editable.toString()) ? editable.toString() : null;
|
||||||
|
getLoaderManager().restartLoader(0, null, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,13 @@ import org.sufficientlysecure.keychain.R;
|
|||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.ActivityOptions;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
@ -40,6 +43,7 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
|
|||||||
|
|
||||||
private TextView mKeyUserId;
|
private TextView mKeyUserId;
|
||||||
private TextView mKeyUserIdRest;
|
private TextView mKeyUserIdRest;
|
||||||
|
private TextView mKeyMasterKeyIdHex;
|
||||||
private BootstrapButton mSelectKeyButton;
|
private BootstrapButton mSelectKeyButton;
|
||||||
private Boolean mFilterCertify;
|
private Boolean mFilterCertify;
|
||||||
|
|
||||||
@ -61,26 +65,52 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
|
|||||||
|
|
||||||
public void selectKey(long secretKeyId) {
|
public void selectKey(long secretKeyId) {
|
||||||
if (secretKeyId == Id.key.none) {
|
if (secretKeyId == Id.key.none) {
|
||||||
mKeyUserId.setText(R.string.api_settings_no_key);
|
mKeyMasterKeyIdHex.setText(R.string.api_settings_no_key);
|
||||||
mKeyUserIdRest.setText("");
|
mKeyUserIdRest.setText("");
|
||||||
|
mKeyUserId.setVisibility(View.GONE);
|
||||||
|
mKeyUserIdRest.setVisibility(View.GONE);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
String uid = getResources().getString(R.string.user_id_no_name);
|
|
||||||
String uidExtra = "";
|
|
||||||
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
|
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
|
||||||
getActivity(), secretKeyId);
|
getActivity(), secretKeyId);
|
||||||
if (keyRing != null) {
|
if (keyRing != null) {
|
||||||
PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
|
PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
|
||||||
|
String masterkeyIdHex = PgpKeyHelper.convertKeyIdToHex(secretKeyId);
|
||||||
|
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key);
|
String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key);
|
||||||
String chunks[] = userId.split(" <", 2);
|
|
||||||
uid = chunks[0];
|
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
||||||
if (chunks.length > 1) {
|
String userName, userEmail;
|
||||||
uidExtra = "<" + chunks[1];
|
|
||||||
|
if (userIdSplit[0] != null) {
|
||||||
|
userName = userIdSplit[0];
|
||||||
|
} else {
|
||||||
|
userName = getActivity().getResources().getString(R.string.user_id_no_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userIdSplit[1] != null) {
|
||||||
|
userEmail = userIdSplit[1];
|
||||||
|
} else {
|
||||||
|
userEmail = getActivity().getResources().getString(R.string.error_user_id_no_email);
|
||||||
|
}
|
||||||
|
|
||||||
|
mKeyMasterKeyIdHex.setText(masterkeyIdHex);
|
||||||
|
mKeyUserId.setText(userName);
|
||||||
|
mKeyUserIdRest.setText(userEmail);
|
||||||
|
mKeyUserId.setVisibility(View.VISIBLE);
|
||||||
|
mKeyUserIdRest.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
mKeyMasterKeyIdHex.setText(getActivity().getResources().getString(R.string.no_key));
|
||||||
|
mKeyUserId.setVisibility(View.GONE);
|
||||||
|
mKeyUserIdRest.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mKeyMasterKeyIdHex.setText(getActivity().getResources().getString(R.string.no_keys_added_or_updated) + " for master id: " + secretKeyId);
|
||||||
|
mKeyUserId.setVisibility(View.GONE);
|
||||||
|
mKeyUserIdRest.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
mKeyUserId.setText(uid);
|
|
||||||
mKeyUserIdRest.setText(uidExtra);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +128,7 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
|
|||||||
|
|
||||||
mKeyUserId = (TextView) view.findViewById(R.id.select_secret_key_user_id);
|
mKeyUserId = (TextView) view.findViewById(R.id.select_secret_key_user_id);
|
||||||
mKeyUserIdRest = (TextView) view.findViewById(R.id.select_secret_key_user_id_rest);
|
mKeyUserIdRest = (TextView) view.findViewById(R.id.select_secret_key_user_id_rest);
|
||||||
|
mKeyMasterKeyIdHex = (TextView) view.findViewById(R.id.select_secret_key_master_key_hex);
|
||||||
mSelectKeyButton = (BootstrapButton) view
|
mSelectKeyButton = (BootstrapButton) view
|
||||||
.findViewById(R.id.select_secret_key_select_key_button);
|
.findViewById(R.id.select_secret_key_select_key_button);
|
||||||
mFilterCertify = false;
|
mFilterCertify = false;
|
||||||
@ -117,30 +148,31 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
|
|||||||
startActivityForResult(intent, REQUEST_CODE_SELECT_KEY);
|
startActivityForResult(intent, REQUEST_CODE_SELECT_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Select Secret Key Activity delivers the intent which was sent by it using interface to Select
|
||||||
|
// Secret Key Fragment.Intent contains Master Key Id, User Email, User Name, Master Key Id Hex.
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
switch (requestCode & 0xFFFF) {
|
switch (requestCode & 0xFFFF) {
|
||||||
case REQUEST_CODE_SELECT_KEY: {
|
case REQUEST_CODE_SELECT_KEY: {
|
||||||
long secretKeyId;
|
long secretKeyId;
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
Bundle bundle = data.getExtras();
|
Bundle bundle = data.getExtras();
|
||||||
secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
|
secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
|
||||||
|
selectKey(secretKeyId);
|
||||||
|
|
||||||
selectKey(secretKeyId);
|
// remove displayed errors
|
||||||
|
mKeyUserId.setError(null);
|
||||||
|
|
||||||
// remove displayed errors
|
// give value back to callback
|
||||||
mKeyUserId.setError(null);
|
mCallback.onKeySelected(secretKeyId);
|
||||||
|
}
|
||||||
// give value back to callback
|
break;
|
||||||
mCallback.onKeySelected(secretKeyId);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ public class UploadKeyActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
// Message is received after uploading is done in ApgService
|
// Message is received after uploading is done in ApgService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
|
getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard ApgHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
@ -113,7 +113,7 @@ public class UploadKeyActivity extends ActionBarActivity {
|
|||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
|
@ -93,12 +93,12 @@ public class ViewKeyActivity extends ActionBarActivity {
|
|||||||
Bundle mainBundle = new Bundle();
|
Bundle mainBundle = new Bundle();
|
||||||
mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
|
mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_main)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_main)),
|
||||||
ViewKeyMainFragment.class, mainBundle, (selectedTab == 0 ? true : false));
|
ViewKeyMainFragment.class, mainBundle, (selectedTab == 0));
|
||||||
|
|
||||||
Bundle certBundle = new Bundle();
|
Bundle certBundle = new Bundle();
|
||||||
certBundle.putLong(ViewKeyCertsFragment.ARG_KEYRING_ROW_ID, rowId);
|
certBundle.putLong(ViewKeyCertsFragment.ARG_KEYRING_ROW_ID, rowId);
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)),
|
||||||
ViewKeyCertsFragment.class, certBundle, (selectedTab == 1 ? true : false));
|
ViewKeyCertsFragment.class, certBundle, (selectedTab == 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -123,8 +123,8 @@ public class ViewKeyActivity extends ActionBarActivity {
|
|||||||
uploadToKeyserver(mDataUri);
|
uploadToKeyserver(mDataUri);
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_key_view_export_file:
|
case R.id.menu_key_view_export_file:
|
||||||
mExportHelper.showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
|
long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
|
||||||
+ "/pubexport.asc");
|
mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_key_view_share_default_fingerprint:
|
case R.id.menu_key_view_share_default_fingerprint:
|
||||||
shareKey(mDataUri, true);
|
shareKey(mDataUri, true);
|
||||||
@ -159,7 +159,7 @@ public class ViewKeyActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateFromKeyserver(Uri dataUri) {
|
private void updateFromKeyserver(Uri dataUri) {
|
||||||
long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, mDataUri);
|
long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, dataUri);
|
||||||
|
|
||||||
if (updateKeyId == 0) {
|
if (updateKeyId == 0) {
|
||||||
Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
|
Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
|
||||||
|
@ -26,7 +26,10 @@ import android.support.v4.app.Fragment;
|
|||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -273,7 +276,7 @@ public class ViewKeyMainFragment extends Fragment implements
|
|||||||
// get key id from MASTER_KEY_ID
|
// get key id from MASTER_KEY_ID
|
||||||
long keyId = data.getLong(KEYS_INDEX_KEY_ID);
|
long keyId = data.getLong(KEYS_INDEX_KEY_ID);
|
||||||
|
|
||||||
String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId);
|
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||||
mKeyId.setText(keyIdStr);
|
mKeyId.setText(keyIdStr);
|
||||||
|
|
||||||
// get creation date from CREATION
|
// get creation date from CREATION
|
||||||
@ -306,9 +309,8 @@ public class ViewKeyMainFragment extends Fragment implements
|
|||||||
fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri);
|
fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri);
|
||||||
}
|
}
|
||||||
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);
|
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);
|
||||||
fingerprint = fingerprint.replace(" ", "\n");
|
|
||||||
|
|
||||||
mFingerprint.setText(fingerprint);
|
mFingerprint.setText(colorizeFingerprint(fingerprint));
|
||||||
}
|
}
|
||||||
|
|
||||||
mKeysAdapter.swapCursor(data);
|
mKeysAdapter.swapCursor(data);
|
||||||
@ -319,6 +321,25 @@ public class ViewKeyMainFragment extends Fragment implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SpannableStringBuilder colorizeFingerprint(String fingerprint) {
|
||||||
|
SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint);
|
||||||
|
ForegroundColorSpan fcs = new ForegroundColorSpan(Color.BLACK);
|
||||||
|
|
||||||
|
// for each 4 characters of the fingerprint + 1 space
|
||||||
|
for (int i = 0; i < fingerprint.length(); i += 5) {
|
||||||
|
int minFingLength = Math.min(i + 4, fingerprint.length());
|
||||||
|
String fourChars = fingerprint.substring(i, minFingLength);
|
||||||
|
|
||||||
|
// Create a foreground color by converting the 4 fingerprint chars to an int hashcode
|
||||||
|
// and then converting that int to hex to use as a color
|
||||||
|
fcs = new ForegroundColorSpan(
|
||||||
|
Color.parseColor(String.format("#%06X", (0xFFFFFF & fourChars.hashCode()))));
|
||||||
|
sb.setSpan(fcs, i, minFingLength, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
|
* This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
|
||||||
* We need to make sure we are no longer using it.
|
* We need to make sure we are no longer using it.
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.ui.adapter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.support.v4.widget.CursorAdapter;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public abstract class HighlightQueryCursorAdapter extends CursorAdapter {
|
||||||
|
|
||||||
|
private String mCurQuery;
|
||||||
|
|
||||||
|
public HighlightQueryCursorAdapter(Context context, Cursor c, int flags) {
|
||||||
|
super(context, c, flags);
|
||||||
|
mCurQuery = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchQuery(String searchQuery) {
|
||||||
|
mCurQuery = searchQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSearchQuery() {
|
||||||
|
return mCurQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Spannable highlightSearchQuery(String text) {
|
||||||
|
Spannable highlight = Spannable.Factory.getInstance().newSpannable(text);
|
||||||
|
|
||||||
|
if (mCurQuery != null) {
|
||||||
|
Pattern pattern = Pattern.compile("(?i)" + mCurQuery);
|
||||||
|
Matcher matcher = pattern.matcher(text);
|
||||||
|
if (matcher.find()) {
|
||||||
|
highlight.setSpan(
|
||||||
|
new ForegroundColorSpan(mContext.getResources().getColor(R.color.emphasis)),
|
||||||
|
matcher.start(),
|
||||||
|
matcher.end(),
|
||||||
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
return highlight;
|
||||||
|
} else {
|
||||||
|
return highlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,7 +42,15 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
|
|||||||
protected Activity mActivity;
|
protected Activity mActivity;
|
||||||
|
|
||||||
protected List<ImportKeysListEntry> data;
|
protected List<ImportKeysListEntry> data;
|
||||||
|
static class ViewHolder{
|
||||||
|
private TextView mainUserId;
|
||||||
|
private TextView mainUserIdRest;
|
||||||
|
private TextView keyId;
|
||||||
|
private TextView fingerprint;
|
||||||
|
private TextView algorithm;
|
||||||
|
private TextView status;
|
||||||
|
|
||||||
|
}
|
||||||
public ImportKeysAdapter(Activity activity) {
|
public ImportKeysAdapter(Activity activity) {
|
||||||
super(activity, -1);
|
super(activity, -1);
|
||||||
mActivity = activity;
|
mActivity = activity;
|
||||||
@ -86,16 +94,21 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
|
|||||||
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
ImportKeysListEntry entry = data.get(position);
|
ImportKeysListEntry entry = data.get(position);
|
||||||
|
ViewHolder holder;
|
||||||
View view = mInflater.inflate(R.layout.import_keys_list_entry, null);
|
if(convertView == null) {
|
||||||
|
holder = new ViewHolder();
|
||||||
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
|
convertView = mInflater.inflate(R.layout.import_keys_list_entry, null);
|
||||||
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
holder.mainUserId = (TextView) convertView.findViewById(R.id.mainUserId);
|
||||||
TextView keyId = (TextView) view.findViewById(R.id.keyId);
|
holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest);
|
||||||
TextView fingerprint = (TextView) view.findViewById(R.id.fingerprint);
|
holder.keyId = (TextView) convertView.findViewById(R.id.keyId);
|
||||||
TextView algorithm = (TextView) view.findViewById(R.id.algorithm);
|
holder.fingerprint = (TextView) convertView.findViewById(R.id.fingerprint);
|
||||||
TextView status = (TextView) view.findViewById(R.id.status);
|
holder.algorithm = (TextView) convertView.findViewById(R.id.algorithm);
|
||||||
|
holder.status = (TextView) convertView.findViewById(R.id.status);
|
||||||
|
convertView.setTag(holder);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
holder = (ViewHolder)convertView.getTag();
|
||||||
|
}
|
||||||
// main user id
|
// main user id
|
||||||
String userId = entry.userIds.get(0);
|
String userId = entry.userIds.get(0);
|
||||||
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
||||||
@ -105,39 +118,39 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
|
|||||||
// show red user id if it is a secret key
|
// show red user id if it is a secret key
|
||||||
if (entry.secretKey) {
|
if (entry.secretKey) {
|
||||||
userIdSplit[0] = mActivity.getString(R.string.secret_key) + " " + userIdSplit[0];
|
userIdSplit[0] = mActivity.getString(R.string.secret_key) + " " + userIdSplit[0];
|
||||||
mainUserId.setTextColor(Color.RED);
|
holder.mainUserId.setTextColor(Color.RED);
|
||||||
}
|
}
|
||||||
mainUserId.setText(userIdSplit[0]);
|
holder.mainUserId.setText(userIdSplit[0]);
|
||||||
} else {
|
} else {
|
||||||
mainUserId.setText(R.string.user_id_no_name);
|
holder.mainUserId.setText(R.string.user_id_no_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// email
|
// email
|
||||||
if (userIdSplit[1] != null) {
|
if (userIdSplit[1] != null) {
|
||||||
mainUserIdRest.setText(userIdSplit[1]);
|
holder.mainUserIdRest.setText(userIdSplit[1]);
|
||||||
mainUserIdRest.setVisibility(View.VISIBLE);
|
holder.mainUserIdRest.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
mainUserIdRest.setVisibility(View.GONE);
|
holder.mainUserIdRest.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
keyId.setText(entry.hexKeyId);
|
holder.keyId.setText(entry.hexKeyId);
|
||||||
|
|
||||||
if (entry.fingerPrint != null) {
|
if (entry.fingerPrint != null) {
|
||||||
fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint);
|
holder.fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint);
|
||||||
fingerprint.setVisibility(View.VISIBLE);
|
holder.fingerprint.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
fingerprint.setVisibility(View.GONE);
|
holder.fingerprint.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
|
holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
|
||||||
|
|
||||||
if (entry.revoked) {
|
if (entry.revoked) {
|
||||||
status.setText(R.string.revoked);
|
holder.status.setText(R.string.revoked);
|
||||||
} else {
|
} else {
|
||||||
status.setVisibility(View.GONE);
|
holder.status.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
LinearLayout ll = (LinearLayout) view.findViewById(R.id.list);
|
LinearLayout ll = (LinearLayout) convertView.findViewById(R.id.list);
|
||||||
if (entry.userIds.size() == 1) {
|
if (entry.userIds.size() == 1) {
|
||||||
ll.setVisibility(View.GONE);
|
ll.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
@ -162,10 +175,10 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckBox cBox = (CheckBox) view.findViewById(R.id.selected);
|
CheckBox cBox = (CheckBox) convertView.findViewById(R.id.selected);
|
||||||
cBox.setChecked(entry.isSelected());
|
cBox.setChecked(entry.isSelected());
|
||||||
|
|
||||||
return view;
|
return convertView;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,10 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
* Constructor for later querying from keyserver
|
* Constructor for later querying from keyserver
|
||||||
*/
|
*/
|
||||||
public ImportKeysListEntry() {
|
public ImportKeysListEntry() {
|
||||||
|
// keys from keyserver are always public keys
|
||||||
secretKey = false;
|
secretKey = false;
|
||||||
|
// do not select by default
|
||||||
|
selected = false;
|
||||||
userIds = new ArrayList<String>();
|
userIds = new ArrayList<String>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +170,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
this.revoked = pgpKeyRing.getPublicKey().isRevoked();
|
this.revoked = pgpKeyRing.getPublicKey().isRevoked();
|
||||||
this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
|
this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
|
||||||
.getFingerprint(), true);
|
.getFingerprint(), true);
|
||||||
this.hexKeyId = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId);
|
this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||||
this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength();
|
this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength();
|
||||||
int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
|
int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
|
||||||
if (algorithm == PGPPublicKey.RSA_ENCRYPT || algorithm == PGPPublicKey.RSA_GENERAL
|
if (algorithm == PGPPublicKey.RSA_ENCRYPT || algorithm == PGPPublicKey.RSA_GENERAL
|
||||||
|
@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.ui.adapter;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
import org.spongycastle.openpgp.PGPKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||||
@ -34,6 +33,21 @@ import android.content.Context;
|
|||||||
import android.support.v4.content.AsyncTaskLoader;
|
import android.support.v4.content.AsyncTaskLoader;
|
||||||
|
|
||||||
public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
||||||
|
|
||||||
|
public static class FileHasNoContent extends Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NonPgpPart extends Exception {
|
||||||
|
private int count;
|
||||||
|
public NonPgpPart(int count) {
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
public int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Context mContext;
|
Context mContext;
|
||||||
|
|
||||||
InputData mInputData;
|
InputData mInputData;
|
||||||
@ -88,21 +102,26 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper
|
|||||||
/**
|
/**
|
||||||
* Reads all PGPKeyRing objects from input
|
* Reads all PGPKeyRing objects from input
|
||||||
*
|
*
|
||||||
* @param keyringBytes
|
* @param inputData
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private void generateListOfKeyrings(InputData inputData) {
|
private void generateListOfKeyrings(InputData inputData) {
|
||||||
|
|
||||||
|
boolean isEmpty = true;
|
||||||
|
int nonPgpCounter = 0;
|
||||||
|
|
||||||
PositionAwareInputStream progressIn = new PositionAwareInputStream(
|
PositionAwareInputStream progressIn = new PositionAwareInputStream(
|
||||||
inputData.getInputStream());
|
inputData.getInputStream());
|
||||||
|
|
||||||
// need to have access to the bufferedInput, so we can reuse it for the possible
|
// need to have access to the bufferedInput, so we can reuse it for the possible
|
||||||
// PGPObject chunks after the first one, e.g. files with several consecutive ASCII
|
// PGPObject chunks after the first one, e.g. files with several consecutive ASCII
|
||||||
// armour blocks
|
// armor blocks
|
||||||
BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
|
BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// read all available blocks... (asc files can contain many blocks with BEGIN END)
|
// read all available blocks... (asc files can contain many blocks with BEGIN END)
|
||||||
while (bufferedInput.available() > 0) {
|
while (bufferedInput.available() > 0) {
|
||||||
|
isEmpty = false;
|
||||||
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
|
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
|
||||||
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
|
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
|
||||||
|
|
||||||
@ -116,11 +135,25 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper
|
|||||||
addToData(newKeyring);
|
addToData(newKeyring);
|
||||||
} else {
|
} else {
|
||||||
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
|
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
|
||||||
|
nonPgpCounter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(Constants.TAG, "Exception on parsing key file!", e);
|
Log.e(Constants.TAG, "Exception on parsing key file!", e);
|
||||||
|
entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(data, e);
|
||||||
|
nonPgpCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isEmpty) {
|
||||||
|
Log.e(Constants.TAG, "File has no content!", new FileHasNoContent());
|
||||||
|
entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
|
||||||
|
(data, new FileHasNoContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nonPgpCounter > 0) {
|
||||||
|
entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
|
||||||
|
(data, new NonPgpPart(nonPgpCounter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.support.v4.widget.CursorAdapter;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -33,7 +32,9 @@ import android.widget.CheckBox;
|
|||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
public class SelectKeyCursorAdapter extends CursorAdapter {
|
|
||||||
|
|
||||||
|
public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter {
|
||||||
|
|
||||||
protected int mKeyType;
|
protected int mKeyType;
|
||||||
|
|
||||||
@ -55,7 +56,6 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
|
|||||||
mInflater = LayoutInflater.from(context);
|
mInflater = LayoutInflater.from(context);
|
||||||
mListView = listView;
|
mListView = listView;
|
||||||
mKeyType = keyType;
|
mKeyType = keyType;
|
||||||
|
|
||||||
initIndex(c);
|
initIndex(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,12 +104,12 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
|
|||||||
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
||||||
|
|
||||||
if (userIdSplit[0] != null) {
|
if (userIdSplit[0] != null) {
|
||||||
mainUserId.setText(userIdSplit[0]);
|
mainUserId.setText(highlightSearchQuery(userIdSplit[0]));
|
||||||
} else {
|
} else {
|
||||||
mainUserId.setText(R.string.user_id_no_name);
|
mainUserId.setText(R.string.user_id_no_name);
|
||||||
}
|
}
|
||||||
if (userIdSplit[1] != null) {
|
if (userIdSplit[1] != null) {
|
||||||
mainUserIdRest.setText(userIdSplit[1]);
|
mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1]));
|
||||||
} else {
|
} else {
|
||||||
mainUserIdRest.setText("");
|
mainUserIdRest.setText("");
|
||||||
}
|
}
|
||||||
@ -164,5 +164,4 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
|
|||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
return mInflater.inflate(R.layout.select_key_item, null);
|
return mInflater.inflate(R.layout.select_key_item, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
|
|||||||
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
|
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
|
||||||
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
|
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
|
||||||
|
|
||||||
String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId));
|
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId));
|
||||||
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm),
|
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm),
|
||||||
cursor.getInt(mIndexKeySize));
|
cursor.getInt(mIndexKeySize));
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.Id;
|
|||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.util.Choice;
|
import org.sufficientlysecure.keychain.util.Choice;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
public class CreateKeyDialogFragment extends DialogFragment {
|
public class CreateKeyDialogFragment extends DialogFragment {
|
||||||
@ -78,7 +79,7 @@ public class CreateKeyDialogFragment extends DialogFragment {
|
|||||||
boolean wouldBeMasterKey = (childCount == 0);
|
boolean wouldBeMasterKey = (childCount == 0);
|
||||||
|
|
||||||
final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm);
|
final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm);
|
||||||
Vector<Choice> choices = new Vector<Choice>();
|
ArrayList<Choice> choices = new ArrayList<Choice>();
|
||||||
choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString(
|
choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString(
|
||||||
R.string.dsa)));
|
R.string.dsa)));
|
||||||
if (!wouldBeMasterKey) {
|
if (!wouldBeMasterKey) {
|
||||||
|
@ -67,7 +67,7 @@ public class DeleteFileDialogFragment extends DialogFragment {
|
|||||||
alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFile));
|
alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFile));
|
||||||
|
|
||||||
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
dismiss();
|
dismiss();
|
||||||
@ -83,7 +83,10 @@ public class DeleteFileDialogFragment extends DialogFragment {
|
|||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance(
|
ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance(
|
||||||
R.string.progress_deleting_securely, ProgressDialog.STYLE_HORIZONTAL, false, null);
|
getString(R.string.progress_deleting_securely),
|
||||||
|
ProgressDialog.STYLE_HORIZONTAL,
|
||||||
|
false,
|
||||||
|
null);
|
||||||
|
|
||||||
// Message is received after deleting is done in ApgService
|
// Message is received after deleting is done in ApgService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(activity, deletingDialog) {
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(activity, deletingDialog) {
|
||||||
@ -95,7 +98,7 @@ public class DeleteFileDialogFragment extends DialogFragment {
|
|||||||
Toast.makeText(activity, R.string.file_delete_successful,
|
Toast.makeText(activity, R.string.file_delete_successful,
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
|
@ -33,7 +33,6 @@ import android.os.Message;
|
|||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
import android.text.Html;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
|
@ -153,17 +153,17 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
dismiss();
|
dismiss();
|
||||||
long curKeyIndex = 1;
|
long curKeyIndex = 1;
|
||||||
boolean keyOK = true;
|
boolean keyOK = true;
|
||||||
String passPhrase = mPassphraseEditText.getText().toString();
|
String passphrase = mPassphraseEditText.getText().toString();
|
||||||
long keyId;
|
long keyId;
|
||||||
PGPSecretKey clickSecretKey = secretKey;
|
PGPSecretKey clickSecretKey = secretKey;
|
||||||
|
|
||||||
if (clickSecretKey != null) {
|
if (clickSecretKey != null) {
|
||||||
while (keyOK == true) {
|
while (keyOK) {
|
||||||
if (clickSecretKey != null) { // check again for loop
|
if (clickSecretKey != null) { // check again for loop
|
||||||
try {
|
try {
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
passPhrase.toCharArray());
|
passphrase.toCharArray());
|
||||||
PGPPrivateKey testKey = clickSecretKey
|
PGPPrivateKey testKey = clickSecretKey
|
||||||
.extractPrivateKey(keyDecryptor);
|
.extractPrivateKey(keyDecryptor);
|
||||||
if (testKey == null) {
|
if (testKey == null) {
|
||||||
@ -206,10 +206,10 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
|
|
||||||
// cache the new passphrase
|
// cache the new passphrase
|
||||||
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
|
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
|
||||||
PassphraseCacheService.addCachedPassphrase(activity, keyId, passPhrase);
|
PassphraseCacheService.addCachedPassphrase(activity, keyId, passphrase);
|
||||||
if (keyOK == false && clickSecretKey.getKeyID() != keyId) {
|
if ( !keyOK && clickSecretKey.getKeyID() != keyId) {
|
||||||
PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(),
|
PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(),
|
||||||
passPhrase);
|
passphrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessageToHandler(MESSAGE_OKAY);
|
sendMessageToHandler(MESSAGE_OKAY);
|
||||||
|
@ -30,7 +30,7 @@ import android.view.KeyEvent;
|
|||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
|
||||||
public class ProgressDialogFragment extends DialogFragment {
|
public class ProgressDialogFragment extends DialogFragment {
|
||||||
private static final String ARG_MESSAGE_ID = "message_id";
|
private static final String ARG_MESSAGE = "message";
|
||||||
private static final String ARG_STYLE = "style";
|
private static final String ARG_STYLE = "style";
|
||||||
private static final String ARG_CANCELABLE = "cancelable";
|
private static final String ARG_CANCELABLE = "cancelable";
|
||||||
|
|
||||||
@ -39,16 +39,16 @@ public class ProgressDialogFragment extends DialogFragment {
|
|||||||
/**
|
/**
|
||||||
* Creates new instance of this fragment
|
* Creates new instance of this fragment
|
||||||
*
|
*
|
||||||
* @param messageId
|
* @param message
|
||||||
* @param style
|
* @param style
|
||||||
* @param cancelable
|
* @param cancelable
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static ProgressDialogFragment newInstance(int messageId, int style, boolean cancelable,
|
public static ProgressDialogFragment newInstance(String message, int style, boolean cancelable,
|
||||||
OnCancelListener onCancelListener) {
|
OnCancelListener onCancelListener) {
|
||||||
ProgressDialogFragment frag = new ProgressDialogFragment();
|
ProgressDialogFragment frag = new ProgressDialogFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putInt(ARG_MESSAGE_ID, messageId);
|
args.putString(ARG_MESSAGE, message);
|
||||||
args.putInt(ARG_STYLE, style);
|
args.putInt(ARG_STYLE, style);
|
||||||
args.putBoolean(ARG_CANCELABLE, cancelable);
|
args.putBoolean(ARG_CANCELABLE, cancelable);
|
||||||
|
|
||||||
@ -117,22 +117,22 @@ public class ProgressDialogFragment extends DialogFragment {
|
|||||||
dialog.setCancelable(false);
|
dialog.setCancelable(false);
|
||||||
dialog.setCanceledOnTouchOutside(false);
|
dialog.setCanceledOnTouchOutside(false);
|
||||||
|
|
||||||
int messageId = getArguments().getInt(ARG_MESSAGE_ID);
|
String message = getArguments().getString(ARG_MESSAGE);
|
||||||
int style = getArguments().getInt(ARG_STYLE);
|
int style = getArguments().getInt(ARG_STYLE);
|
||||||
boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE);
|
boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE);
|
||||||
|
|
||||||
dialog.setMessage(getString(messageId));
|
dialog.setMessage(message);
|
||||||
dialog.setProgressStyle(style);
|
dialog.setProgressStyle(style);
|
||||||
|
|
||||||
if (cancelable) {
|
if (cancelable) {
|
||||||
dialog.setButton(DialogInterface.BUTTON_NEGATIVE,
|
dialog.setButton(DialogInterface.BUTTON_NEGATIVE,
|
||||||
activity.getString(R.string.progress_cancel),
|
activity.getString(R.string.progress_cancel),
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
dialog.cancel();
|
dialog.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable the back button
|
// Disable the back button
|
||||||
@ -140,7 +140,6 @@ public class ProgressDialogFragment extends DialogFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||||
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -101,9 +101,9 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
|||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
dismiss();
|
dismiss();
|
||||||
|
|
||||||
String passPhrase1 = mPassphraseEditText.getText().toString();
|
String passphrase1 = mPassphraseEditText.getText().toString();
|
||||||
String passPhrase2 = mPassphraseAgainEditText.getText().toString();
|
String passphrase2 = mPassphraseAgainEditText.getText().toString();
|
||||||
if (!passPhrase1.equals(passPhrase2)) {
|
if (!passphrase1.equals(passphrase2)) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
activity,
|
activity,
|
||||||
getString(R.string.error_message,
|
getString(R.string.error_message,
|
||||||
@ -112,7 +112,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passPhrase1.equals("")) {
|
if (passphrase1.equals("")) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
activity,
|
activity,
|
||||||
getString(R.string.error_message,
|
getString(R.string.error_message,
|
||||||
@ -123,7 +123,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
|||||||
|
|
||||||
// return resulting data back to activity
|
// return resulting data back to activity
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
data.putString(MESSAGE_NEW_PASSPHRASE, passPhrase1);
|
data.putString(MESSAGE_NEW_PASSPHRASE, passphrase1);
|
||||||
|
|
||||||
sendMessageToHandler(MESSAGE_OKAY, data);
|
sendMessageToHandler(MESSAGE_OKAY, data);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,6 @@ public class ShareNfcDialogFragment extends DialogFragment {
|
|||||||
|
|
||||||
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
||||||
|
|
||||||
alert.setIcon(android.R.drawable.ic_dialog_info);
|
|
||||||
alert.setTitle(R.string.share_nfc_dialog);
|
alert.setTitle(R.string.share_nfc_dialog);
|
||||||
alert.setCancelable(true);
|
alert.setCancelable(true);
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ import android.app.DatePickerDialog;
|
|||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
@ -58,6 +59,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
|||||||
Spinner mUsage;
|
Spinner mUsage;
|
||||||
TextView mCreationDate;
|
TextView mCreationDate;
|
||||||
BootstrapButton mExpiryDateButton;
|
BootstrapButton mExpiryDateButton;
|
||||||
|
GregorianCalendar mCreatedDate;
|
||||||
GregorianCalendar mExpiryDate;
|
GregorianCalendar mExpiryDate;
|
||||||
|
|
||||||
private int mDatePickerResultCount = 0;
|
private int mDatePickerResultCount = 0;
|
||||||
@ -113,8 +115,12 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
|||||||
if (date == null) {
|
if (date == null) {
|
||||||
date = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
date = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
DatePickerDialog dialog = new DatePickerDialog(getContext(),
|
* Using custom DatePickerDialog which overrides the setTitle because
|
||||||
|
* the DatePickerDialog title is buggy (unix warparound bug).
|
||||||
|
* See: https://code.google.com/p/android/issues/detail?id=49066
|
||||||
|
*/
|
||||||
|
DatePickerDialog dialog = new ExpiryDatePickerDialog(getContext(),
|
||||||
mExpiryDateSetListener, date.get(Calendar.YEAR), date.get(Calendar.MONTH),
|
mExpiryDateSetListener, date.get(Calendar.YEAR), date.get(Calendar.MONTH),
|
||||||
date.get(Calendar.DAY_OF_MONTH));
|
date.get(Calendar.DAY_OF_MONTH));
|
||||||
mDatePickerResultCount = 0;
|
mDatePickerResultCount = 0;
|
||||||
@ -129,6 +135,21 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// setCalendarViewShown() is supported from API 11 onwards.
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
// Hide calendarView in tablets because of the unix warparound bug.
|
||||||
|
dialog.getDatePicker().setCalendarViewShown(false);
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
if ( dialog != null && mCreatedDate != null ) {
|
||||||
|
dialog.getDatePicker().setMinDate(mCreatedDate.getTime().getTime()+ DateUtils.DAY_IN_MILLIS);
|
||||||
|
} else {
|
||||||
|
//When created date isn't available
|
||||||
|
dialog.getDatePicker().setMinDate(date.getTime().getTime()+ DateUtils.DAY_IN_MILLIS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -153,9 +174,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(key));
|
mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(key));
|
||||||
String keyId1Str = PgpKeyHelper.convertKeyIdToHex(key.getKeyID());
|
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyID());
|
||||||
String keyId2Str = PgpKeyHelper.convertKeyIdToHex(key.getKeyID() >> 32);
|
mKeyId.setText(keyIdStr);
|
||||||
mKeyId.setText(keyId1Str + " " + keyId2Str);
|
|
||||||
|
|
||||||
Vector<Choice> choices = new Vector<Choice>();
|
Vector<Choice> choices = new Vector<Choice>();
|
||||||
boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
|
boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
|
||||||
@ -205,7 +225,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
|||||||
|
|
||||||
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||||
cal.setTime(PgpKeyHelper.getCreationDate(key));
|
cal.setTime(PgpKeyHelper.getCreationDate(key));
|
||||||
mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime()));
|
setCreatedDate(cal);
|
||||||
cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||||
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
|
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
|
||||||
if (expiryDate == null) {
|
if (expiryDate == null) {
|
||||||
@ -235,6 +255,15 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
|||||||
mEditorListener = listener;
|
mEditorListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setCreatedDate(GregorianCalendar date) {
|
||||||
|
mCreatedDate = date;
|
||||||
|
if (date == null) {
|
||||||
|
mCreationDate.setText(getContext().getString(R.string.none));
|
||||||
|
} else {
|
||||||
|
mCreationDate.setText(DateFormat.getDateInstance().format(date.getTime()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setExpiryDate(GregorianCalendar date) {
|
private void setExpiryDate(GregorianCalendar date) {
|
||||||
mExpiryDate = date;
|
mExpiryDate = date;
|
||||||
if (date == null) {
|
if (date == null) {
|
||||||
@ -253,3 +282,14 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ExpiryDatePickerDialog extends DatePickerDialog {
|
||||||
|
|
||||||
|
public ExpiryDatePickerDialog(Context context, OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) {
|
||||||
|
super(context, callBack, year, monthOfYear, dayOfMonth);
|
||||||
|
}
|
||||||
|
//Set permanent title.
|
||||||
|
public void setTitle(CharSequence title) {
|
||||||
|
super.setTitle(getContext().getString(R.string.expiry_date_dialog_title));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -80,19 +80,19 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
|||||||
public void setType(int type) {
|
public void setType(int type) {
|
||||||
mType = type;
|
mType = type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Id.type.user_id: {
|
case Id.type.user_id: {
|
||||||
mTitle.setText(R.string.section_user_ids);
|
mTitle.setText(R.string.section_user_ids);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Id.type.key: {
|
case Id.type.key: {
|
||||||
mTitle.setText(R.string.section_keys);
|
mTitle.setText(R.string.section_keys);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onFinishInflate() {
|
protected void onFinishInflate() {
|
||||||
mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
@ -121,7 +123,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
|||||||
super.onFinishInflate();
|
super.onFinishInflate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public void onDeleted(Editor editor) {
|
public void onDeleted(Editor editor) {
|
||||||
this.updateEditorsVisible();
|
this.updateEditorsVisible();
|
||||||
}
|
}
|
||||||
@ -131,38 +135,40 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
|||||||
mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
|
mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (canEdit) {
|
if (canEdit) {
|
||||||
switch (mType) {
|
switch (mType) {
|
||||||
case Id.type.user_id: {
|
case Id.type.user_id: {
|
||||||
UserIdEditor view = (UserIdEditor) mInflater.inflate(
|
UserIdEditor view = (UserIdEditor) mInflater.inflate(
|
||||||
R.layout.edit_key_user_id_item, mEditors, false);
|
R.layout.edit_key_user_id_item, mEditors, false);
|
||||||
view.setEditorListener(this);
|
view.setEditorListener(this);
|
||||||
if (mEditors.getChildCount() == 0) {
|
if (mEditors.getChildCount() == 0) {
|
||||||
view.setIsMainUserId(true);
|
view.setIsMainUserId(true);
|
||||||
}
|
|
||||||
mEditors.addView(view);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Id.type.key: {
|
|
||||||
CreateKeyDialogFragment mCreateKeyDialogFragment = CreateKeyDialogFragment.newInstance(mEditors.getChildCount());
|
|
||||||
mCreateKeyDialogFragment.setOnAlgorithmSelectedListener(new CreateKeyDialogFragment.OnAlgorithmSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void onAlgorithmSelected(Choice algorithmChoice, int keySize) {
|
|
||||||
mNewKeyAlgorithmChoice = algorithmChoice;
|
|
||||||
mNewKeySize = keySize;
|
|
||||||
createKey();
|
|
||||||
}
|
}
|
||||||
});
|
mEditors.addView(view);
|
||||||
mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog");
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
case Id.type.key: {
|
||||||
break;
|
CreateKeyDialogFragment mCreateKeyDialogFragment = CreateKeyDialogFragment.newInstance(mEditors.getChildCount());
|
||||||
}
|
mCreateKeyDialogFragment.setOnAlgorithmSelectedListener(new CreateKeyDialogFragment.OnAlgorithmSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onAlgorithmSelected(Choice algorithmChoice, int keySize) {
|
||||||
|
mNewKeyAlgorithmChoice = algorithmChoice;
|
||||||
|
mNewKeySize = keySize;
|
||||||
|
createKey();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.updateEditorsVisible();
|
this.updateEditorsVisible();
|
||||||
}
|
}
|
||||||
@ -220,31 +226,34 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
|||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
Boolean isMasterKey;
|
Boolean isMasterKey;
|
||||||
|
|
||||||
String passPhrase;
|
String passphrase;
|
||||||
if (mEditors.getChildCount() > 0) {
|
if (mEditors.getChildCount() > 0) {
|
||||||
PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
|
PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
|
||||||
passPhrase = PassphraseCacheService
|
passphrase = PassphraseCacheService
|
||||||
.getCachedPassphrase(mActivity, masterKey.getKeyID());
|
.getCachedPassphrase(mActivity, masterKey.getKeyID());
|
||||||
isMasterKey = false;
|
isMasterKey = false;
|
||||||
} else {
|
} else {
|
||||||
passPhrase = "";
|
passphrase = "";
|
||||||
isMasterKey = true;
|
isMasterKey = true;
|
||||||
}
|
}
|
||||||
data.putBoolean(KeychainIntentService.GENERATE_KEY_MASTER_KEY, isMasterKey);
|
data.putBoolean(KeychainIntentService.GENERATE_KEY_MASTER_KEY, isMasterKey);
|
||||||
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passPhrase);
|
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase);
|
||||||
data.putInt(KeychainIntentService.GENERATE_KEY_ALGORITHM, mNewKeyAlgorithmChoice.getId());
|
data.putInt(KeychainIntentService.GENERATE_KEY_ALGORITHM, mNewKeyAlgorithmChoice.getId());
|
||||||
data.putInt(KeychainIntentService.GENERATE_KEY_KEY_SIZE, mNewKeySize);
|
data.putInt(KeychainIntentService.GENERATE_KEY_KEY_SIZE, mNewKeySize);
|
||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// show progress dialog
|
// show progress dialog
|
||||||
mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating,
|
mGeneratingDialog = ProgressDialogFragment.newInstance(
|
||||||
ProgressDialog.STYLE_SPINNER, true, new DialogInterface.OnCancelListener() {
|
getResources().getQuantityString(R.plurals.progress_generating, 1),
|
||||||
@Override
|
ProgressDialog.STYLE_SPINNER,
|
||||||
public void onCancel(DialogInterface dialog) {
|
true,
|
||||||
mActivity.stopService(intent);
|
new DialogInterface.OnCancelListener() {
|
||||||
}
|
@Override
|
||||||
});
|
public void onCancel(DialogInterface dialog) {
|
||||||
|
mActivity.stopService(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Message is received after generating is done in ApgService
|
// Message is received after generating is done in ApgService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity,
|
||||||
@ -261,7 +270,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
|||||||
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
|
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
|
||||||
addGeneratedKeyToView(newKey);
|
addGeneratedKeyToView(newKey);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
|
@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.widget;
|
|||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import android.widget.*;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -26,11 +27,9 @@ import android.util.AttributeSet;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.RadioButton;
|
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
|
import org.sufficientlysecure.keychain.helper.ContactHelper;
|
||||||
|
|
||||||
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
|
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
|
||||||
private EditorListener mEditorListener = null;
|
private EditorListener mEditorListener = null;
|
||||||
@ -38,7 +37,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
|
|||||||
private BootstrapButton mDeleteButton;
|
private BootstrapButton mDeleteButton;
|
||||||
private RadioButton mIsMainUserId;
|
private RadioButton mIsMainUserId;
|
||||||
private EditText mName;
|
private EditText mName;
|
||||||
private EditText mEmail;
|
private AutoCompleteTextView mEmail;
|
||||||
private EditText mComment;
|
private EditText mComment;
|
||||||
|
|
||||||
// see http://www.regular-expressions.info/email.html
|
// see http://www.regular-expressions.info/email.html
|
||||||
@ -102,9 +101,17 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
|
|||||||
mIsMainUserId.setOnClickListener(this);
|
mIsMainUserId.setOnClickListener(this);
|
||||||
|
|
||||||
mName = (EditText) findViewById(R.id.name);
|
mName = (EditText) findViewById(R.id.name);
|
||||||
mEmail = (EditText) findViewById(R.id.email);
|
mEmail = (AutoCompleteTextView) findViewById(R.id.email);
|
||||||
mComment = (EditText) findViewById(R.id.comment);
|
mComment = (EditText) findViewById(R.id.comment);
|
||||||
|
|
||||||
|
|
||||||
|
mEmail.setThreshold(1); // Start working from first character
|
||||||
|
mEmail.setAdapter(
|
||||||
|
new ArrayAdapter<String>
|
||||||
|
(this.getContext(), android.R.layout.simple_dropdown_item_1line,
|
||||||
|
ContactHelper.getMailAccounts(getContext())
|
||||||
|
));
|
||||||
|
|
||||||
super.onFinishInflate();
|
super.onFinishInflate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
* Copyright (C) 2011 Thialfihar <thi@thialfihar.org>
|
||||||
* Copyright (C) 2011 Senecaso
|
* Copyright (C) 2011 Senecaso
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
@ -224,7 +226,7 @@ public class HkpKeyServer extends KeyServer {
|
|||||||
HttpClient client = new DefaultHttpClient();
|
HttpClient client = new DefaultHttpClient();
|
||||||
try {
|
try {
|
||||||
HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
|
HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
|
||||||
+ "/pks/lookup?op=get&search=0x" + PgpKeyHelper.convertKeyToHex(keyId));
|
+ "/pks/lookup?op=get&search=" + PgpKeyHelper.convertKeyIdToHex(keyId));
|
||||||
|
|
||||||
HttpResponse response = client.execute(get);
|
HttpResponse response = client.execute(get);
|
||||||
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
|
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
* Copyright (C) 2011 Thialfihar <thi@thialfihar.org>
|
||||||
* Copyright (C) 2011 Senecaso
|
* Copyright (C) 2011 Senecaso
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
@ -16,14 +18,8 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.util;
|
package org.sufficientlysecure.keychain.util;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||||
|
|
||||||
public abstract class KeyServer {
|
public abstract class KeyServer {
|
||||||
@ -52,5 +48,5 @@ public abstract class KeyServer {
|
|||||||
|
|
||||||
abstract String get(long keyId) throws QueryException;
|
abstract String get(long keyId) throws QueryException;
|
||||||
|
|
||||||
abstract void add(String armouredText) throws AddKeyException;
|
abstract void add(String armoredText) throws AddKeyException;
|
||||||
}
|
}
|
||||||
|
BIN
OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_save.png
Normal file
After Width: | Height: | Size: 398 B |