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
|
||||
|
||||
# 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"
|
||||
script: gradle assemble -S -q
|
||||
|
||||
|
@ -5,7 +5,7 @@ buildscript {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion "19.0.1"
|
||||
buildToolsVersion "19.0.3"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
|
@ -202,7 +202,7 @@ public class OpenPgpProviderActivity extends Activity {
|
||||
break;
|
||||
}
|
||||
case OpenPgpApi.RESULT_CODE_ERROR: {
|
||||
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERRORS);
|
||||
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
|
||||
handleError(error);
|
||||
break;
|
||||
}
|
||||
@ -234,7 +234,7 @@ public class OpenPgpProviderActivity extends Activity {
|
||||
}
|
||||
|
||||
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_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
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
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 {
|
||||
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 {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion '19.0.1'
|
||||
buildToolsVersion '19.0.3'
|
||||
|
||||
// NOTE: We are using the old folder structure to also support Eclipse
|
||||
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;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public boolean isSignatureOnly() {
|
||||
return signatureOnly;
|
||||
}
|
||||
|
||||
public void setSignatureOnly(boolean signatureOnly) {
|
||||
this.signatureOnly = signatureOnly;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public long getKeyId() {
|
||||
return keyId;
|
||||
}
|
||||
|
||||
public void setKeyId(long keyId) {
|
||||
this.keyId = keyId;
|
||||
}
|
||||
|
||||
public OpenPgpSignatureResult() {
|
||||
|
||||
}
|
||||
|
||||
public OpenPgpSignatureResult(int signatureStatus, String signatureUserId,
|
||||
boolean signatureOnly, long keyId) {
|
||||
boolean signatureOnly, long keyId) {
|
||||
this.status = signatureStatus;
|
||||
this.signatureOnly = signatureOnly;
|
||||
this.userId = signatureUserId;
|
@ -16,119 +16,146 @@
|
||||
|
||||
package org.openintents.openpgp.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import org.openintents.openpgp.IOpenPgpService;
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class OpenPgpApi {
|
||||
|
||||
//TODO: fix this documentation
|
||||
/**
|
||||
* General extras
|
||||
* --------------
|
||||
*
|
||||
* Intent extras:
|
||||
* int api_version (required)
|
||||
* boolean ascii_armor (request ascii armor for ouput)
|
||||
*
|
||||
* returned Bundle:
|
||||
* int result_code (0, 1, or 2 (see OpenPgpApi))
|
||||
* OpenPgpError error (if result_code == 0)
|
||||
* Intent intent (if result_code == 2)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sign only
|
||||
*
|
||||
* optional params:
|
||||
* String passphrase (for key passphrase)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Encrypt
|
||||
*
|
||||
* Intent extras:
|
||||
* long[] key_ids
|
||||
* or
|
||||
* String[] user_ids (= emails of recipients) (if more than one key has this user_id, a PendingIntent is returned)
|
||||
*
|
||||
* optional extras:
|
||||
* String passphrase (for key passphrase)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sign and encrypt
|
||||
*
|
||||
* Intent extras:
|
||||
* same as in encrypt()
|
||||
*/
|
||||
|
||||
/**
|
||||
* Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
|
||||
* and also signed-only input.
|
||||
*
|
||||
* returned Bundle:
|
||||
* OpenPgpSignatureResult signature_result
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieves key ids based on given user ids (=emails)
|
||||
*
|
||||
* Intent extras:
|
||||
* String[] user_ids
|
||||
*
|
||||
* returned Bundle:
|
||||
* long[] 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";
|
||||
|
||||
/**
|
||||
* General extras
|
||||
* --------------
|
||||
*
|
||||
* required extras:
|
||||
* int EXTRA_API_VERSION (always required)
|
||||
*
|
||||
* returned extras:
|
||||
* int RESULT_CODE (RESULT_CODE_ERROR, RESULT_CODE_SUCCESS or RESULT_CODE_USER_INTERACTION_REQUIRED)
|
||||
* OpenPgpError RESULT_ERROR (if RESULT_CODE == RESULT_CODE_ERROR)
|
||||
* PendingIntent RESULT_INTENT (if RESULT_CODE == RESULT_CODE_USER_INTERACTION_REQUIRED)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sign only
|
||||
*
|
||||
* optional extras:
|
||||
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
|
||||
* String EXTRA_PASSPHRASE (key passphrase)
|
||||
*/
|
||||
public static final String ACTION_SIGN = "org.openintents.openpgp.action.SIGN";
|
||||
|
||||
/**
|
||||
* Encrypt
|
||||
*
|
||||
* required extras:
|
||||
* String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT)
|
||||
* or
|
||||
* long[] EXTRA_KEY_IDS
|
||||
*
|
||||
* optional extras:
|
||||
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
|
||||
* String EXTRA_PASSPHRASE (key passphrase)
|
||||
*/
|
||||
public static final String ACTION_ENCRYPT = "org.openintents.openpgp.action.ENCRYPT";
|
||||
public static final String ACTION_SIGN_AND_ENCTYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT";
|
||||
|
||||
/**
|
||||
* Sign and encrypt
|
||||
*
|
||||
* required extras:
|
||||
* String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT)
|
||||
* or
|
||||
* long[] EXTRA_KEY_IDS
|
||||
*
|
||||
* optional extras:
|
||||
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput)
|
||||
* String EXTRA_PASSPHRASE (key passphrase)
|
||||
*/
|
||||
public static final String ACTION_SIGN_AND_ENCRYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT";
|
||||
|
||||
/**
|
||||
* Decrypts and verifies given input stream. This methods handles encrypted-only, signed-and-encrypted,
|
||||
* and also signed-only input.
|
||||
*
|
||||
* If OpenPgpSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY
|
||||
* 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";
|
||||
public static final String ACTION_DOWNLOAD_KEYS = "org.openintents.openpgp.action.DOWNLOAD_KEYS";
|
||||
|
||||
/**
|
||||
* Get key ids based on given user ids (=emails)
|
||||
*
|
||||
* required extras:
|
||||
* String[] EXTRA_USER_IDS
|
||||
*
|
||||
* returned extras:
|
||||
* long[] EXTRA_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";
|
||||
|
||||
// SIGN, ENCRYPT, SIGN_ENCRYPT, DECRYPT_VERIFY
|
||||
// SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY
|
||||
// request ASCII Armor for output
|
||||
// OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
|
||||
public static final String EXTRA_REQUEST_ASCII_ARMOR = "ascii_armor";
|
||||
|
||||
// ENCRYPT, SIGN_ENCRYPT
|
||||
// ENCRYPT, SIGN_AND_ENCRYPT
|
||||
public static final String EXTRA_USER_IDS = "user_ids";
|
||||
public static final String EXTRA_KEY_IDS = "key_ids";
|
||||
// optional parameter:
|
||||
// optional extras:
|
||||
public static final String EXTRA_PASSPHRASE = "passphrase";
|
||||
|
||||
/* Service Bundle returns */
|
||||
public static final String RESULT_CODE = "result_code";
|
||||
public static final String RESULT_SIGNATURE = "signature";
|
||||
public static final String RESULT_ERRORS = "error";
|
||||
public static final String RESULT_INTENT = "intent";
|
||||
// GET_KEY
|
||||
public static final String EXTRA_KEY_ID = "key_id";
|
||||
|
||||
// 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;
|
||||
// success!
|
||||
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 String RESULT_ERROR = "error";
|
||||
public static final String RESULT_INTENT = "intent";
|
||||
|
||||
// DECRYPT_VERIFY
|
||||
public static final String RESULT_SIGNATURE = "signature";
|
||||
|
||||
IOpenPgpService mService;
|
||||
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) {
|
||||
OpenPgpAsyncTask task = new OpenPgpAsyncTask(data, is, os, callback);
|
||||
|
||||
@ -188,13 +216,13 @@ public class OpenPgpApi {
|
||||
result = mService.execute(data, null, null);
|
||||
return result;
|
||||
} else {
|
||||
// send the input and output pfds
|
||||
// pipe the input and output
|
||||
ParcelFileDescriptor input = ParcelFileDescriptorUtil.pipeFrom(is,
|
||||
new ParcelFileDescriptorUtil.IThreadListener() {
|
||||
|
||||
@Override
|
||||
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,
|
||||
@ -202,7 +230,7 @@ public class OpenPgpApi {
|
||||
|
||||
@Override
|
||||
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);
|
||||
Intent result = new Intent();
|
||||
result.putExtra(RESULT_CODE, RESULT_CODE_ERROR);
|
||||
result.putExtra(RESULT_ERRORS,
|
||||
result.putExtra(RESULT_ERROR,
|
||||
new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage()));
|
||||
return result;
|
||||
}
|
@ -31,7 +31,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.TextView;
|
||||
import org.sufficientlysecure.keychain.api.R;
|
||||
import org.openintents.openpgp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
@ -1,2 +1,3 @@
|
||||
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 {
|
||||
compile 'com.android.support:support-v4: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:StickyListHeaders:library')
|
||||
compile project(':libraries:AndroidBootstrap')
|
||||
@ -18,7 +19,7 @@ dependencies {
|
||||
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion "19.0.1"
|
||||
buildToolsVersion "19.0.3"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
|
@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.sufficientlysecure.keychain"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="23101"
|
||||
android:versionName="2.3.1 beta1">
|
||||
android:versionCode="23104"
|
||||
android:versionName="2.3.1 beta4">
|
||||
|
||||
<!--
|
||||
General remarks
|
||||
@ -22,7 +22,8 @@
|
||||
Remarks about the ugly android:pathPattern:
|
||||
- We are matching all files with a specific file ending.
|
||||
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.
|
||||
- Do _not_ set mimeType for gpg!
|
||||
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.WRITE_EXTERNAL_STORAGE" />
|
||||
<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! -->
|
||||
<application
|
||||
@ -106,30 +108,12 @@
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_select_recipients"
|
||||
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
|
||||
android:name=".ui.SelectSecretKeyActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_select_secret_key"
|
||||
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
|
||||
android:name=".ui.EncryptActivity"
|
||||
@ -261,7 +245,16 @@
|
||||
<activity
|
||||
android:name=".ui.PreferencesActivity"
|
||||
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
|
||||
android:name=".ui.PreferencesKeyServerActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
@ -439,10 +432,6 @@
|
||||
<!--<intent-filter>-->
|
||||
<!--<action android:name="org.sufficientlysecure.keychain.service.remote.IExtendedApiService" />-->
|
||||
<!--</intent-filter>-->
|
||||
|
||||
<!--<meta-data-->
|
||||
<!--android:name="api_version"-->
|
||||
<!--android:value="1" />-->
|
||||
<!--</service>-->
|
||||
|
||||
<!-- TODO: authority! Make this API with content provider uris -->
|
||||
|
@ -43,6 +43,8 @@ public final class Constants {
|
||||
public static final class path {
|
||||
public static final String APP_DIR = Environment.getExternalStorageDirectory()
|
||||
+ "/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 {
|
||||
@ -51,7 +53,7 @@ public final class Constants {
|
||||
public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour";
|
||||
public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression";
|
||||
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 FORCE_V3_SIGNATURES = "forceV3Signatures";
|
||||
public static final String KEY_SERVERS = "keyServers";
|
||||
|
@ -30,7 +30,7 @@ public final class Id {
|
||||
public static final class menu {
|
||||
|
||||
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 about = 0x21070003;
|
||||
public static final int manage_public_keys = 0x21070004;
|
||||
@ -85,12 +85,12 @@ public final class Id {
|
||||
}
|
||||
|
||||
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 decrypting = 0x21070003;
|
||||
public static final int new_pass_phrase = 0x21070004;
|
||||
public static final int pass_phrases_do_not_match = 0x21070005;
|
||||
public static final int no_pass_phrase = 0x21070006;
|
||||
public static final int new_passphrase = 0x21070004;
|
||||
public static final int passphrases_do_not_match = 0x21070005;
|
||||
public static final int no_passphrase = 0x21070006;
|
||||
public static final int saving = 0x21070007;
|
||||
public static final int delete_key = 0x21070008;
|
||||
public static final int import_keys = 0x21070009;
|
||||
|
@ -59,7 +59,6 @@ public class ClipboardReflection {
|
||||
* Wrapper around ClipboardManager based on Android version using Reflection API
|
||||
*
|
||||
* @param context
|
||||
* @param text
|
||||
*/
|
||||
public static CharSequence getClipboardText(Context context) {
|
||||
Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
@ -56,28 +56,33 @@ public class ActionBarHelper {
|
||||
* Sets custom view on ActionBar for Done/Cancel activities
|
||||
*
|
||||
* @param actionBar
|
||||
* @param doneText
|
||||
* @param doneOnClickListener
|
||||
* @param cancelText
|
||||
* @param cancelOnClickListener
|
||||
* @param firstText
|
||||
* @param firstDrawableId
|
||||
* @param firstOnClickListener
|
||||
* @param secondText
|
||||
* @param secondDrawableId
|
||||
* @param secondOnClickListener
|
||||
*/
|
||||
public static void setDoneCancelView(ActionBar actionBar, int doneText,
|
||||
OnClickListener doneOnClickListener, int cancelText,
|
||||
OnClickListener cancelOnClickListener) {
|
||||
public static void setTwoButtonView(ActionBar actionBar, int firstText, int firstDrawableId,
|
||||
OnClickListener firstOnClickListener, int secondText, int secondDrawableId,
|
||||
OnClickListener secondOnClickListener) {
|
||||
|
||||
// Inflate a "Done"/"Cancel" custom action bar view
|
||||
// Inflate the custom action bar view
|
||||
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
||||
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
||||
final View customActionBarView = inflater.inflate(
|
||||
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(
|
||||
doneOnClickListener);
|
||||
((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text))
|
||||
.setText(cancelText);
|
||||
firstOnClickListener);
|
||||
TextView secondTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text));
|
||||
secondTextView.setText(secondText);
|
||||
secondTextView.setCompoundDrawablesWithIntrinsicBounds(secondDrawableId, 0, 0, 0);
|
||||
customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
|
||||
cancelOnClickListener);
|
||||
secondOnClickListener);
|
||||
|
||||
// Show the custom action bar view and hide the normal Home icon and title.
|
||||
actionBar.setDisplayShowTitleEnabled(false);
|
||||
@ -91,20 +96,22 @@ public class ActionBarHelper {
|
||||
* Sets custom view on ActionBar for Done activities
|
||||
*
|
||||
* @param actionBar
|
||||
* @param doneText
|
||||
* @param doneOnClickListener
|
||||
* @param firstText
|
||||
* @param firstOnClickListener
|
||||
*/
|
||||
public static void setDoneView(ActionBar actionBar, int doneText,
|
||||
OnClickListener doneOnClickListener) {
|
||||
public static void setOneButtonView(ActionBar actionBar, int firstText, int firstDrawableId,
|
||||
OnClickListener firstOnClickListener) {
|
||||
// Inflate a "Done" custom action bar view to serve as the "Up" affordance.
|
||||
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
||||
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
||||
final View customActionBarView = inflater
|
||||
.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(
|
||||
doneOnClickListener);
|
||||
firstOnClickListener);
|
||||
|
||||
// Show the custom action bar view and hide the normal Home icon and title.
|
||||
actionBar.setDisplayShowTitleEnabled(false);
|
||||
@ -112,5 +119,4 @@ public class ActionBarHelper {
|
||||
actionBar.setDisplayShowCustomEnabled(true);
|
||||
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.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||
@ -63,7 +62,7 @@ public class ExportHelper {
|
||||
/**
|
||||
* 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) {
|
||||
mExportFilename = exportFilename;
|
||||
|
||||
@ -75,7 +74,7 @@ public class ExportHelper {
|
||||
Bundle data = message.getData();
|
||||
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() {
|
||||
public void run() {
|
||||
String title = null;
|
||||
if (dataUri == null) {
|
||||
if (rowIds == null) {
|
||||
// export all keys
|
||||
title = activity.getString(R.string.title_export_keys);
|
||||
} else {
|
||||
@ -112,7 +111,7 @@ public class ExportHelper {
|
||||
/**
|
||||
* Export keys
|
||||
*/
|
||||
public void exportKeys(Uri dataUri, int keyType) {
|
||||
public void exportKeys(long[] rowIds, int keyType) {
|
||||
Log.d(Constants.TAG, "exportKeys started");
|
||||
|
||||
// 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.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
|
||||
|
||||
if (dataUri == null) {
|
||||
if (rowIds == null) {
|
||||
data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
|
||||
} else {
|
||||
// TODO: put data uri into service???
|
||||
long keyRingMasterKeyId = ProviderHelper.getMasterKeyId(activity, dataUri);
|
||||
|
||||
data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId);
|
||||
data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_ROW_ID, rowIds);
|
||||
}
|
||||
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
|
||||
// Message is received after exporting is done in ApgService
|
||||
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) {
|
||||
// handle messages by standard ApgHandler first
|
||||
super.handleMessage(message);
|
||||
@ -160,7 +156,7 @@ public class ExportHelper {
|
||||
Toast.makeText(activity, toastMessage, Toast.LENGTH_SHORT).show();
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
|
@ -17,8 +17,6 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.helper;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -144,8 +144,8 @@ public class Preferences {
|
||||
Constants.defaults.KEY_SERVERS);
|
||||
Vector<String> servers = new Vector<String>();
|
||||
String chunks[] = rawData.split(",");
|
||||
for (int i = 0; i < chunks.length; ++i) {
|
||||
String tmp = chunks[i].trim();
|
||||
for (String c : chunks) {
|
||||
String tmp = c.trim();
|
||||
if (tmp.length() > 0) {
|
||||
servers.add(tmp);
|
||||
}
|
||||
@ -156,8 +156,8 @@ public class Preferences {
|
||||
public void setKeyServers(String[] value) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
String rawData = "";
|
||||
for (int i = 0; i < value.length; ++i) {
|
||||
String tmp = value[i].trim();
|
||||
for (String v : value) {
|
||||
String tmp = v.trim();
|
||||
if (tmp.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ public class PgpConversionHelper {
|
||||
*
|
||||
* Singles keys are encoded as keyRings with one single key in it by Bouncy Castle
|
||||
*
|
||||
* @param keysBytes
|
||||
* @param keyBytes
|
||||
* @return
|
||||
*/
|
||||
public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
|
||||
@ -149,7 +149,7 @@ public class PgpConversionHelper {
|
||||
/**
|
||||
* Convert from PGPSecretKey to byte[]
|
||||
*
|
||||
* @param keysBytes
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
|
||||
@ -165,7 +165,7 @@ public class PgpConversionHelper {
|
||||
/**
|
||||
* Convert from PGPSecretKeyRing to byte[]
|
||||
*
|
||||
* @param keysBytes
|
||||
* @param keyRing
|
||||
* @return
|
||||
*/
|
||||
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
|
||||
|
@ -18,8 +18,8 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.spongycastle.bcpg.ArmoredInputStream;
|
||||
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
||||
import org.spongycastle.openpgp.PGPCompressedData;
|
||||
@ -36,6 +36,7 @@ import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureList;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||
@ -53,7 +54,7 @@ import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
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.Log;
|
||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||
@ -75,9 +76,10 @@ public class PgpDecryptVerify {
|
||||
private InputData data;
|
||||
private OutputStream outStream;
|
||||
|
||||
private ProgressDialogUpdater progress;
|
||||
boolean assumeSymmetric;
|
||||
String passphrase;
|
||||
private ProgressDialogUpdater progressDialogUpdater;
|
||||
private boolean assumeSymmetric;
|
||||
private String passphrase;
|
||||
private long enforcedKeyId;
|
||||
|
||||
private PgpDecryptVerify(Builder builder) {
|
||||
// private Constructor can only be called from Builder
|
||||
@ -85,9 +87,10 @@ public class PgpDecryptVerify {
|
||||
this.data = builder.data;
|
||||
this.outStream = builder.outStream;
|
||||
|
||||
this.progress = builder.progress;
|
||||
this.progressDialogUpdater = builder.progressDialogUpdater;
|
||||
this.assumeSymmetric = builder.assumeSymmetric;
|
||||
this.passphrase = builder.passphrase;
|
||||
this.enforcedKeyId = builder.enforcedKeyId;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
@ -97,9 +100,10 @@ public class PgpDecryptVerify {
|
||||
private OutputStream outStream;
|
||||
|
||||
// optional
|
||||
private ProgressDialogUpdater progress = null;
|
||||
private ProgressDialogUpdater progressDialogUpdater = null;
|
||||
private boolean assumeSymmetric = false;
|
||||
private String passphrase = "";
|
||||
private long enforcedKeyId = 0;
|
||||
|
||||
public Builder(Context context, InputData data, OutputStream outStream) {
|
||||
this.context = context;
|
||||
@ -107,8 +111,8 @@ public class PgpDecryptVerify {
|
||||
this.outStream = outStream;
|
||||
}
|
||||
|
||||
public Builder progress(ProgressDialogUpdater progress) {
|
||||
this.progress = progress;
|
||||
public Builder progressDialogUpdater(ProgressDialogUpdater progressDialogUpdater) {
|
||||
this.progressDialogUpdater = progressDialogUpdater;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -122,20 +126,32 @@ public class PgpDecryptVerify {
|
||||
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() {
|
||||
return new PgpDecryptVerify(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateProgress(int message, int current, int total) {
|
||||
if (progress != null) {
|
||||
progress.setProgress(message, current, total);
|
||||
if (progressDialogUpdater != null) {
|
||||
progressDialogUpdater.setProgress(message, current, total);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateProgress(int current, int total) {
|
||||
if (progress != null) {
|
||||
progress.setProgress(current, total);
|
||||
if (progressDialogUpdater != null) {
|
||||
progressDialogUpdater.setProgress(current, total);
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,9 +193,8 @@ public class PgpDecryptVerify {
|
||||
* @throws PGPException
|
||||
* @throws SignatureException
|
||||
*/
|
||||
public Bundle execute()
|
||||
public PgpDecryptVerifyResult execute()
|
||||
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||
|
||||
// automatically works with ascii armor input and binary
|
||||
InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
|
||||
if (in instanceof ArmoredInputStream) {
|
||||
@ -207,9 +222,9 @@ public class PgpDecryptVerify {
|
||||
* @throws PGPException
|
||||
* @throws SignatureException
|
||||
*/
|
||||
private Bundle decryptVerify(InputStream in)
|
||||
private PgpDecryptVerifyResult decryptVerify(InputStream in)
|
||||
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||
Bundle returnData = new Bundle();
|
||||
PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult();
|
||||
|
||||
PGPObjectFactory pgpF = new PGPObjectFactory(in);
|
||||
PGPEncryptedDataList enc;
|
||||
@ -277,9 +292,40 @@ public class PgpDecryptVerify {
|
||||
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
|
||||
secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID());
|
||||
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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,7 +335,7 @@ public class PgpDecryptVerify {
|
||||
|
||||
currentProgress += 5;
|
||||
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
|
||||
PGPPrivateKey privateKey = null;
|
||||
PGPPrivateKey privateKey;
|
||||
try {
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||
@ -317,6 +363,7 @@ public class PgpDecryptVerify {
|
||||
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
|
||||
Object dataChunk = plainFact.nextObject();
|
||||
PGPOnePassSignature signature = null;
|
||||
OpenPgpSignatureResult signatureResult = null;
|
||||
PGPPublicKey signatureKey = null;
|
||||
int signatureIndex = -1;
|
||||
|
||||
@ -334,7 +381,7 @@ public class PgpDecryptVerify {
|
||||
if (dataChunk instanceof PGPOnePassSignatureList) {
|
||||
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
|
||||
|
||||
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
|
||||
signatureResult = new OpenPgpSignatureResult();
|
||||
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
|
||||
for (int i = 0; i < sigList.size(); ++i) {
|
||||
signature = sigList.get(i);
|
||||
@ -354,12 +401,12 @@ public class PgpDecryptVerify {
|
||||
if (signKeyRing != null) {
|
||||
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
||||
}
|
||||
returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
|
||||
signatureResult.setUserId(userId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
|
||||
signatureResult.setKeyId(signatureKeyId);
|
||||
|
||||
if (signature != null) {
|
||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
||||
@ -367,7 +414,7 @@ public class PgpDecryptVerify {
|
||||
|
||||
signature.init(contentVerifierBuilderProvider, signatureKey);
|
||||
} else {
|
||||
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
|
||||
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY);
|
||||
}
|
||||
|
||||
dataChunk = plainFact.nextObject();
|
||||
@ -405,8 +452,7 @@ public class PgpDecryptVerify {
|
||||
try {
|
||||
signature.update(buffer, 0, n);
|
||||
} catch (SignatureException e) {
|
||||
returnData
|
||||
.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
|
||||
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
|
||||
signature = null;
|
||||
}
|
||||
}
|
||||
@ -430,17 +476,20 @@ public class PgpDecryptVerify {
|
||||
PGPSignature messageSignature = signatureList.get(signatureIndex);
|
||||
|
||||
// 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
|
||||
boolean keyBinding_isok = verifyKeyBinding(context, messageSignature, signatureKey);
|
||||
boolean sig_isok = signature.verify(messageSignature);
|
||||
boolean validKeyBinding = verifyKeyBinding(context, messageSignature, signatureKey);
|
||||
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()) {
|
||||
updateProgress(R.string.progress_verifying_integrity, 95, 100);
|
||||
|
||||
@ -455,9 +504,12 @@ public class PgpDecryptVerify {
|
||||
} else {
|
||||
// no integrity check
|
||||
Log.e(Constants.TAG, "Encrypted data was not integrity protected!");
|
||||
// TODO: inform user?
|
||||
}
|
||||
|
||||
updateProgress(R.string.progress_done, 100, 100);
|
||||
|
||||
returnData.setSignatureResult(signatureResult);
|
||||
return returnData;
|
||||
}
|
||||
|
||||
@ -474,11 +526,12 @@ public class PgpDecryptVerify {
|
||||
* @throws PGPException
|
||||
* @throws SignatureException
|
||||
*/
|
||||
private Bundle verifyCleartextSignature(ArmoredInputStream aIn)
|
||||
private PgpDecryptVerifyResult verifyCleartextSignature(ArmoredInputStream aIn)
|
||||
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||
Bundle returnData = new Bundle();
|
||||
PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult();
|
||||
OpenPgpSignatureResult signatureResult = new OpenPgpSignatureResult();
|
||||
// cleartext signatures are never encrypted ;)
|
||||
returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, true);
|
||||
signatureResult.setSignatureOnly(true);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
@ -504,8 +557,6 @@ public class PgpDecryptVerify {
|
||||
byte[] clearText = out.toByteArray();
|
||||
outStream.write(clearText);
|
||||
|
||||
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
|
||||
|
||||
updateProgress(R.string.progress_processing_signature, 60, 100);
|
||||
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
|
||||
|
||||
@ -533,15 +584,17 @@ public class PgpDecryptVerify {
|
||||
if (signKeyRing != null) {
|
||||
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
||||
}
|
||||
returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
|
||||
signatureResult.setUserId(userId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
|
||||
signatureResult.setKeyId(signatureKeyId);
|
||||
|
||||
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);
|
||||
return returnData;
|
||||
}
|
||||
@ -569,12 +622,17 @@ public class PgpDecryptVerify {
|
||||
} while (lookAhead != -1);
|
||||
}
|
||||
|
||||
boolean sig_isok = signature.verify();
|
||||
|
||||
//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);
|
||||
return returnData;
|
||||
@ -582,34 +640,34 @@ public class PgpDecryptVerify {
|
||||
|
||||
private static boolean verifyKeyBinding(Context context, PGPSignature signature, PGPPublicKey signatureKey) {
|
||||
long signatureKeyId = signature.getKeyID();
|
||||
boolean keyBinding_isok = false;
|
||||
String userId = null;
|
||||
boolean validKeyBinding = false;
|
||||
|
||||
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
|
||||
signatureKeyId);
|
||||
PGPPublicKey mKey = null;
|
||||
if (signKeyRing != null) {
|
||||
mKey = PgpKeyHelper.getMasterKey(signKeyRing);
|
||||
}
|
||||
|
||||
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
|
||||
keyBinding_isok = true;
|
||||
validKeyBinding = true;
|
||||
}
|
||||
return keyBinding_isok;
|
||||
return validKeyBinding;
|
||||
}
|
||||
|
||||
private static boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
||||
boolean subkeyBinding_isok = false;
|
||||
boolean tmp_subkeyBinding_isok = false;
|
||||
boolean primkeyBinding_isok = false;
|
||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
boolean validSubkeyBinding = false;
|
||||
boolean validTempSubkeyBinding = false;
|
||||
boolean validPrimaryKeyBinding = false;
|
||||
|
||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||
new JcaPGPContentVerifierBuilderProvider()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
|
||||
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?
|
||||
//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
|
||||
@ -619,32 +677,36 @@ public class PgpDecryptVerify {
|
||||
//check and if ok, check primary key binding.
|
||||
try {
|
||||
sig.init(contentVerifierBuilderProvider, masterPublicKey);
|
||||
tmp_subkeyBinding_isok = sig.verifyCertification(masterPublicKey, signingPublicKey);
|
||||
validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey);
|
||||
} catch (PGPException e) {
|
||||
continue;
|
||||
} catch (SignatureException e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tmp_subkeyBinding_isok)
|
||||
subkeyBinding_isok = true;
|
||||
if (tmp_subkeyBinding_isok) {
|
||||
primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), masterPublicKey, signingPublicKey);
|
||||
if (primkeyBinding_isok)
|
||||
if (validTempSubkeyBinding)
|
||||
validSubkeyBinding = true;
|
||||
if (validTempSubkeyBinding) {
|
||||
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
|
||||
masterPublicKey, signingPublicKey);
|
||||
if (validPrimaryKeyBinding)
|
||||
break;
|
||||
primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), masterPublicKey, signingPublicKey);
|
||||
if (primkeyBinding_isok)
|
||||
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
|
||||
masterPublicKey, signingPublicKey);
|
||||
if (validPrimaryKeyBinding)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (subkeyBinding_isok & primkeyBinding_isok);
|
||||
return (validSubkeyBinding & validPrimaryKeyBinding);
|
||||
}
|
||||
|
||||
private static boolean verifyPrimaryBinding(PGPSignatureSubpacketVector Pkts, PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
||||
boolean primkeyBinding_isok = false;
|
||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector Pkts,
|
||||
PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
||||
boolean validPrimaryKeyBinding = false;
|
||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||
new JcaPGPContentVerifierBuilderProvider()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureList eSigList;
|
||||
|
||||
if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
|
||||
@ -660,8 +722,8 @@ public class PgpDecryptVerify {
|
||||
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
|
||||
try {
|
||||
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
|
||||
primkeyBinding_isok = emSig.verifyCertification(masterPublicKey, signingPublicKey);
|
||||
if (primkeyBinding_isok)
|
||||
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
|
||||
if (validPrimaryKeyBinding)
|
||||
break;
|
||||
} catch (PGPException e) {
|
||||
continue;
|
||||
@ -671,7 +733,8 @@ public class PgpDecryptVerify {
|
||||
}
|
||||
}
|
||||
}
|
||||
return primkeyBinding_isok;
|
||||
|
||||
return validPrimaryKeyBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -680,10 +743,9 @@ public class PgpDecryptVerify {
|
||||
* @param sig
|
||||
* @param line
|
||||
* @throws SignatureException
|
||||
* @throws IOException
|
||||
*/
|
||||
private static void processLine(PGPSignature sig, byte[] line)
|
||||
throws SignatureException, IOException {
|
||||
throws SignatureException {
|
||||
int length = getLengthWithoutWhiteSpace(line);
|
||||
if (length > 0) {
|
||||
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 progress
|
||||
* @param file
|
||||
* @throws FileNotFoundException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file)
|
||||
throws FileNotFoundException, IOException {
|
||||
throws IOException {
|
||||
long length = file.length();
|
||||
SecureRandom random = new SecureRandom();
|
||||
RandomAccessFile raf = new RandomAccessFile(file, "rws");
|
||||
|
@ -17,11 +17,9 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -29,12 +27,10 @@ import java.util.List;
|
||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
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.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.util.HkpKeyServer;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
|
||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||
|
||||
import android.content.Context;
|
||||
@ -85,13 +79,14 @@ public class PgpImportExport {
|
||||
|
||||
public boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
|
||||
ArmoredOutputStream aos = null;
|
||||
try {
|
||||
aos = new ArmoredOutputStream(bos);
|
||||
aos.write(keyring.getEncoded());
|
||||
aos.close();
|
||||
|
||||
String armouredKey = bos.toString("UTF-8");
|
||||
server.add(armouredKey);
|
||||
String armoredKey = bos.toString("UTF-8");
|
||||
server.add(armoredKey);
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
@ -101,7 +96,8 @@ public class PgpImportExport {
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
bos.close();
|
||||
if (aos != null) aos.close();
|
||||
if (bos != null) bos.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
@ -161,59 +157,53 @@ public class PgpImportExport {
|
||||
return returnData;
|
||||
}
|
||||
|
||||
public Bundle exportKeyRings(ArrayList<Long> keyRingMasterKeyIds, int keyType,
|
||||
OutputStream outStream) throws PgpGeneralException, FileNotFoundException,
|
||||
public Bundle exportKeyRings(ArrayList<Long> keyRingRowIds, int keyType,
|
||||
OutputStream outStream) throws PgpGeneralException,
|
||||
PGPException, IOException {
|
||||
Bundle returnData = new Bundle();
|
||||
|
||||
int rowIdsSize = keyRingRowIds.size();
|
||||
|
||||
updateProgress(
|
||||
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
|
||||
keyRingMasterKeyIds.size()), 0, 100);
|
||||
rowIdsSize), 0, 100);
|
||||
|
||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
throw new PgpGeneralException(
|
||||
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) {
|
||||
ArmoredOutputStream outSec = new ArmoredOutputStream(outStream);
|
||||
outSec.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
||||
|
||||
for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
|
||||
updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
|
||||
|
||||
PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
|
||||
mContext, keyRingMasterKeyIds.get(i));
|
||||
// If the keyType is secret get the PGPSecretKeyRing
|
||||
// based on the row id and encode it to the output
|
||||
if (keyType == Id.type.secret_key) {
|
||||
updateProgress(i * 100 / rowIdsSize / 2, 100);
|
||||
PGPSecretKeyRing secretKeyRing =
|
||||
ProviderHelper.getPGPSecretKeyRingByRowId(mContext, keyRingRowIds.get(i));
|
||||
|
||||
if (secretKeyRing != null) {
|
||||
secretKeyRing.encode(outSec);
|
||||
secretKeyRing.encode(arOutStream);
|
||||
}
|
||||
}
|
||||
outSec.close();
|
||||
} else {
|
||||
// export public keyrings...
|
||||
ArmoredOutputStream outPub = new ArmoredOutputStream(outStream);
|
||||
outPub.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
||||
|
||||
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));
|
||||
// Else if it's a public key get the PGPPublicKeyRing
|
||||
// and encode that to the output
|
||||
} else {
|
||||
updateProgress(i * 100 / rowIdsSize, 100);
|
||||
PGPPublicKeyRing publicKeyRing =
|
||||
ProviderHelper.getPGPPublicKeyRingByRowId(mContext, keyRingRowIds.get(i));
|
||||
|
||||
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);
|
||||
|
||||
@ -234,7 +224,7 @@ public class PgpImportExport {
|
||||
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
|
||||
secretKeyRing.getSecretKeys())) {
|
||||
if (!testSecretKey.isMasterKey()) {
|
||||
if (PgpKeyHelper.isSecretKeyPrivateEmpty(testSecretKey)) {
|
||||
if (testSecretKey.isPrivateKeyEmpty()) {
|
||||
// this is bad, something is very wrong...
|
||||
save = false;
|
||||
status = Id.return_value.bad;
|
||||
|
@ -32,6 +32,7 @@ import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
@ -415,28 +416,27 @@ public class PgpKeyHelper {
|
||||
String algorithmStr = null;
|
||||
|
||||
switch (algorithm) {
|
||||
case PGPPublicKey.RSA_ENCRYPT:
|
||||
case PGPPublicKey.RSA_GENERAL:
|
||||
case PGPPublicKey.RSA_SIGN: {
|
||||
algorithmStr = "RSA";
|
||||
break;
|
||||
}
|
||||
case PGPPublicKey.RSA_ENCRYPT:
|
||||
case PGPPublicKey.RSA_GENERAL:
|
||||
case PGPPublicKey.RSA_SIGN: {
|
||||
algorithmStr = "RSA";
|
||||
break;
|
||||
}
|
||||
case PGPPublicKey.DSA: {
|
||||
algorithmStr = "DSA";
|
||||
break;
|
||||
}
|
||||
|
||||
case PGPPublicKey.DSA: {
|
||||
algorithmStr = "DSA";
|
||||
break;
|
||||
}
|
||||
case PGPPublicKey.ELGAMAL_ENCRYPT:
|
||||
case PGPPublicKey.ELGAMAL_GENERAL: {
|
||||
algorithmStr = "ElGamal";
|
||||
break;
|
||||
}
|
||||
|
||||
case PGPPublicKey.ELGAMAL_ENCRYPT:
|
||||
case PGPPublicKey.ELGAMAL_GENERAL: {
|
||||
algorithmStr = "ElGamal";
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
algorithmStr = "Unknown";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
algorithmStr = "Unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(keySize > 0)
|
||||
return algorithmStr + ", " + keySize + " bit";
|
||||
@ -444,31 +444,6 @@ public class PgpKeyHelper {
|
||||
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) {
|
||||
PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId);
|
||||
// if it is no public key get it from your own keys...
|
||||
@ -484,41 +459,57 @@ public class PgpKeyHelper {
|
||||
return convertFingerprintToHex(key.getFingerprint(), true);
|
||||
}
|
||||
|
||||
public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) {
|
||||
return secretKey.isPrivateKeyEmpty();
|
||||
}
|
||||
|
||||
// public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) {
|
||||
// PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
|
||||
// if (secretKey == null) {
|
||||
// Log.e(Constants.TAG, "Key could not be found!");
|
||||
// return false; // could be a public key, assume it is not empty
|
||||
// }
|
||||
// return isSecretKeyPrivateEmpty(secretKey);
|
||||
// }
|
||||
|
||||
public static String convertKeyIdToHex(long keyId) {
|
||||
String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US);
|
||||
while (fingerPrint.length() < 8) {
|
||||
fingerPrint = "0" + fingerPrint;
|
||||
/**
|
||||
* Converts fingerprint to hex (optional: with whitespaces after 4 characters)
|
||||
* <p/>
|
||||
* Fingerprint is shown using lowercase characters. Studies have shown that humans can
|
||||
* better differentiate between numbers and letters when letters are lowercase.
|
||||
*
|
||||
* @param fingerprint
|
||||
* @param split split into 4 character chunks
|
||||
* @return
|
||||
*/
|
||||
public static String convertFingerprintToHex(byte[] fingerprint, boolean split) {
|
||||
String hexString = Hex.toHexString(fingerprint);
|
||||
if (split) {
|
||||
hexString = hexString.replaceAll("(.{4})(?!$)", "$1 ");
|
||||
}
|
||||
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
|
||||
* @return
|
||||
*/
|
||||
public static String convertKeyToHex(long keyId) {
|
||||
return convertKeyIdToHex(keyId >> 32) + convertKeyIdToHex(keyId);
|
||||
public static String convertKeyIdToHex(long keyId) {
|
||||
return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
|
||||
}
|
||||
|
||||
public static long convertHexToKeyId(String data) {
|
||||
int len = data.length();
|
||||
String s2 = data.substring(len - 8);
|
||||
String s1 = data.substring(0, len - 8);
|
||||
private static String convertKeyIdToHex32bit(long keyId) {
|
||||
String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.US);
|
||||
while (hexString.length() < 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);
|
||||
}
|
||||
|
||||
@ -529,7 +520,7 @@ public class PgpKeyHelper {
|
||||
* @return array with naming (0), email (1), comment (2)
|
||||
*/
|
||||
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("")) {
|
||||
return result;
|
||||
@ -550,7 +541,6 @@ public class PgpKeyHelper {
|
||||
result[0] = matcher.group(1);
|
||||
result[1] = matcher.group(3);
|
||||
result[2] = matcher.group(2);
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -107,7 +107,7 @@ public class PgpKeyOperation {
|
||||
*
|
||||
* @param algorithmChoice
|
||||
* @param keySize
|
||||
* @param passPhrase
|
||||
* @param passphrase
|
||||
* @param isMasterKey
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
@ -118,7 +118,7 @@ public class PgpKeyOperation {
|
||||
*/
|
||||
|
||||
// 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,
|
||||
PgpGeneralException, InvalidAlgorithmParameterException {
|
||||
|
||||
@ -126,8 +126,8 @@ public class PgpKeyOperation {
|
||||
throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
|
||||
}
|
||||
|
||||
if (passPhrase == null) {
|
||||
passPhrase = "";
|
||||
if (passphrase == null) {
|
||||
passphrase = "";
|
||||
}
|
||||
|
||||
int algorithm = 0;
|
||||
@ -181,7 +181,7 @@ public class PgpKeyOperation {
|
||||
// Build key encrypter and decrypter based on passphrase
|
||||
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
|
||||
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(),
|
||||
sha1Calc, isMasterKey, keyEncryptor);
|
||||
@ -190,7 +190,7 @@ public class PgpKeyOperation {
|
||||
}
|
||||
|
||||
public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
|
||||
String newPassPhrase) throws IOException, PGPException, PGPException,
|
||||
String newPassPhrase) throws IOException, PGPException,
|
||||
NoSuchProviderException {
|
||||
|
||||
updateProgress(R.string.progress_building_key, 0, 100);
|
||||
|
@ -342,7 +342,7 @@ public class KeychainProvider extends ContentProvider {
|
||||
/**
|
||||
* Returns type of the query (secret/public)
|
||||
*
|
||||
* @param uri
|
||||
* @param match
|
||||
* @return
|
||||
*/
|
||||
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
|
||||
* where clause with rowId
|
||||
*
|
||||
* @param uri
|
||||
* @param defaultSelection
|
||||
* @param keyType
|
||||
* @param selection
|
||||
* @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,
|
||||
long masterKeyId) {
|
||||
@ -123,11 +123,8 @@ public class ProviderHelper {
|
||||
*/
|
||||
public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long 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) {
|
||||
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 {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
boolean has_private = true;
|
||||
boolean hasPrivate = true;
|
||||
if (key.isMasterKey()) {
|
||||
if (PgpKeyHelper.isSecretKeyPrivateEmpty(key)) {
|
||||
has_private = false;
|
||||
if (key.isPrivateKeyEmpty()) {
|
||||
hasPrivate = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,8 +416,8 @@ public class ProviderHelper {
|
||||
values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
|
||||
values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
|
||||
values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
|
||||
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && has_private));
|
||||
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && has_private));
|
||||
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && hasPrivate));
|
||||
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && hasPrivate));
|
||||
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
|
||||
values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
|
||||
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
|
||||
@ -480,6 +474,30 @@ public class ProviderHelper {
|
||||
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
|
||||
*/
|
||||
@ -496,6 +514,22 @@ public class ProviderHelper {
|
||||
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) {
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null);
|
||||
@ -722,7 +756,7 @@ public class ProviderHelper {
|
||||
|
||||
String armoredKey = bos.toString("UTF-8");
|
||||
|
||||
Log.d(Constants.TAG, "armouredKey:" + armoredKey);
|
||||
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
|
||||
|
||||
output.add(armoredKey);
|
||||
} catch (IOException e) {
|
||||
|
@ -44,11 +44,13 @@ import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
|
||||
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.DataStream;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
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_ALL = "export_all";
|
||||
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
|
||||
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
|
||||
@ -181,13 +184,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
// decrypt/verify
|
||||
public static final String RESULT_DECRYPTED_STRING = "decrypted_message";
|
||||
public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
|
||||
public static final String RESULT_SIGNATURE = "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";
|
||||
public static final String RESULT_DECRYPT_VERIFY_RESULT = "signature";
|
||||
|
||||
// import
|
||||
public static final String RESULT_IMPORT_ADDED = "added";
|
||||
@ -206,7 +203,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
private boolean mIsCanceled;
|
||||
|
||||
public KeychainIntentService() {
|
||||
super("ApgService");
|
||||
super("KeychainIntentService");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -489,15 +486,17 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
// verifyText and decrypt returning additional resultData values for the
|
||||
// verification of signatures
|
||||
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream);
|
||||
builder.progress(this);
|
||||
builder.progressDialogUpdater(this);
|
||||
|
||||
builder.assumeSymmetric(assumeSymmetricEncryption)
|
||||
.passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
||||
|
||||
resultData = builder.build().execute();
|
||||
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
|
||||
|
||||
outStream.close();
|
||||
|
||||
resultData.putParcelable(RESULT_DECRYPT_VERIFY_RESULT, decryptVerifyResult);
|
||||
|
||||
/* Output */
|
||||
|
||||
switch (target) {
|
||||
@ -599,13 +598,23 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
|
||||
|
||||
/* Operation */
|
||||
int keysTotal = 2;
|
||||
int keysCreated = 0;
|
||||
setProgress(
|
||||
getApplicationContext().getResources().getQuantityString(R.plurals.progress_generating, keysTotal),
|
||||
keysCreated,
|
||||
keysTotal);
|
||||
PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
|
||||
|
||||
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
||||
4096, passphrase, true);
|
||||
keysCreated++;
|
||||
setProgress(keysCreated, keysTotal);
|
||||
|
||||
PGPSecretKey subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
||||
4096, passphrase, false);
|
||||
keysCreated++;
|
||||
setProgress(keysCreated, keysTotal);
|
||||
|
||||
// TODO: default to one master for cert, one sub for encrypt and one sub
|
||||
// for sign
|
||||
@ -668,10 +677,12 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
|
||||
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);
|
||||
long keyRingMasterKeyId = -1;
|
||||
if (!exportAll) {
|
||||
keyRingMasterKeyId = data.getLong(EXPORT_KEY_RING_MASTER_KEY_ID);
|
||||
rowIds = data.getLongArray(EXPORT_KEY_RING_ROW_ID);
|
||||
}
|
||||
|
||||
/* Operation */
|
||||
@ -684,24 +695,26 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
// OutputStream
|
||||
FileOutputStream outStream = new FileOutputStream(outputFile);
|
||||
|
||||
ArrayList<Long> keyRingMasterKeyIds = new ArrayList<Long>();
|
||||
ArrayList<Long> keyRingRowIds = new ArrayList<Long>();
|
||||
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) {
|
||||
keyRingMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this);
|
||||
keyRingRowIds = ProviderHelper.getPublicKeyRingsRowIds(this);
|
||||
} else {
|
||||
keyRingMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this);
|
||||
keyRingRowIds = ProviderHelper.getSecretKeyRingsRowIds(this);
|
||||
}
|
||||
} else {
|
||||
keyRingMasterKeyIds.add(keyRingMasterKeyId);
|
||||
for(long rowId : rowIds) {
|
||||
keyRingRowIds.add(rowId);
|
||||
}
|
||||
}
|
||||
|
||||
Bundle resultData = new Bundle();
|
||||
Bundle resultData;
|
||||
|
||||
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
|
||||
resultData = pgpImportExport
|
||||
.exportKeyRings(keyRingMasterKeyIds, keyType, outStream);
|
||||
.exportKeyRings(keyRingRowIds, keyType, outStream);
|
||||
|
||||
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
|
||||
} 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
|
||||
// PGPObject chunks after the first one, e.g. files with several consecutive ASCII
|
||||
// armour blocks
|
||||
// armor blocks
|
||||
BufferedInputStream bufferedInput = new BufferedInputStream(new ByteArrayInputStream(downloadedKey));
|
||||
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) {
|
||||
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);
|
||||
|
||||
Bundle data = new Bundle();
|
||||
|
@ -21,7 +21,6 @@ import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@ -51,21 +50,26 @@ public class KeychainIntentServiceHandler extends Handler {
|
||||
this.mActivity = activity;
|
||||
}
|
||||
|
||||
public KeychainIntentServiceHandler(Activity activity, ProgressDialogFragment progressDialogFragment) {
|
||||
public KeychainIntentServiceHandler(Activity activity,
|
||||
ProgressDialogFragment progressDialogFragment) {
|
||||
this.mActivity = activity;
|
||||
this.mProgressDialogFragment = progressDialogFragment;
|
||||
}
|
||||
|
||||
public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) {
|
||||
this(activity, progressDialogMessageId, progressDialogStyle, false, null);
|
||||
public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage,
|
||||
int progressDialogStyle) {
|
||||
this(activity, progressDialogMessage, progressDialogStyle, false, null);
|
||||
}
|
||||
|
||||
public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId,
|
||||
public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage,
|
||||
int progressDialogStyle, boolean cancelable,
|
||||
OnCancelListener onCancelListener) {
|
||||
this.mActivity = activity;
|
||||
this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId,
|
||||
progressDialogStyle, cancelable, onCancelListener);
|
||||
this.mProgressDialogFragment = ProgressDialogFragment.newInstance(
|
||||
progressDialogMessage,
|
||||
progressDialogStyle,
|
||||
cancelable,
|
||||
onCancelListener);
|
||||
}
|
||||
|
||||
public void showProgressDialog(FragmentActivity activity) {
|
||||
@ -84,43 +88,43 @@ public class KeychainIntentServiceHandler extends Handler {
|
||||
Bundle data = message.getData();
|
||||
|
||||
switch (message.arg1) {
|
||||
case MESSAGE_OKAY:
|
||||
mProgressDialogFragment.dismissAllowingStateLoss();
|
||||
case MESSAGE_OKAY:
|
||||
mProgressDialogFragment.dismissAllowingStateLoss();
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
case MESSAGE_EXCEPTION:
|
||||
mProgressDialogFragment.dismissAllowingStateLoss();
|
||||
case MESSAGE_EXCEPTION:
|
||||
mProgressDialogFragment.dismissAllowingStateLoss();
|
||||
|
||||
// show error from service
|
||||
if (data.containsKey(DATA_ERROR)) {
|
||||
Toast.makeText(mActivity,
|
||||
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
|
||||
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));
|
||||
// show error from service
|
||||
if (data.containsKey(DATA_ERROR)) {
|
||||
Toast.makeText(mActivity,
|
||||
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
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;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import android.util.LongSparseArray;
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
@ -77,7 +78,7 @@ public class PassphraseCacheService extends Service {
|
||||
|
||||
private BroadcastReceiver mIntentReceiver;
|
||||
|
||||
private HashMap<Long, String> mPassphraseCache = new HashMap<Long, String>();
|
||||
private LongSparseArray<String> mPassphraseCache = new LongSparseArray<String>();
|
||||
|
||||
Context mContext;
|
||||
|
||||
@ -347,7 +348,7 @@ public class PassphraseCacheService extends Service {
|
||||
Log.d(TAG, "Timeout of keyId " + keyId + ", removed from memory!");
|
||||
|
||||
// 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!");
|
||||
stopSelf();
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public class AppSettingsActivity extends ActionBarActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// 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() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -21,7 +21,6 @@ import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
@ -33,9 +32,11 @@ import org.spongycastle.util.Arrays;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Id;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
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.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
@ -103,9 +104,8 @@ public class OpenPgpService extends RemoteService {
|
||||
|
||||
// return PendingIntent to be executed by client
|
||||
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_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -114,8 +114,8 @@ public class OpenPgpService extends RemoteService {
|
||||
}
|
||||
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
result.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keyIdsArray);
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -130,9 +130,8 @@ public class OpenPgpService extends RemoteService {
|
||||
|
||||
// return PendingIntent to be executed by client
|
||||
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_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -179,9 +178,9 @@ public class OpenPgpService extends RemoteService {
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Intent result = new Intent();
|
||||
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()));
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -208,9 +207,9 @@ public class OpenPgpService extends RemoteService {
|
||||
}
|
||||
} else {
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||
result.putExtra(OpenPgpApi.RESULT_ERRORS,
|
||||
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||
new OpenPgpError(OpenPgpError.GENERIC_ERROR, "Missing parameter user_ids or key_ids!"));
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -267,9 +266,9 @@ public class OpenPgpService extends RemoteService {
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Intent result = new Intent();
|
||||
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()));
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -284,98 +283,30 @@ public class OpenPgpService extends RemoteService {
|
||||
Intent result = new Intent();
|
||||
try {
|
||||
|
||||
// TODO:
|
||||
// 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;
|
||||
}
|
||||
|
||||
String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
|
||||
long inputLength = is.available();
|
||||
InputData inputData = new InputData(is, inputLength);
|
||||
|
||||
Bundle outputBundle;
|
||||
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
|
||||
|
||||
builder.assumeSymmetric(false)
|
||||
builder.assumeSymmetric(false) // no support for symmetric encryption
|
||||
.enforcedKeyId(appSettings.getKeyId()) // allow only the private key for this app for decryption
|
||||
.passphrase(passphrase);
|
||||
|
||||
// TODO: this also decrypts with other secret keys that have no passphrase!!!
|
||||
outputBundle = builder.build().execute();
|
||||
// TODO: currently does not support binary signed-only content
|
||||
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
|
||||
|
||||
//TODO: instead of using all these wrapping use OpenPgpSignatureResult directly
|
||||
// in DecryptVerify class and then in DecryptActivity
|
||||
boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE, false);
|
||||
if (signature) {
|
||||
long signatureKeyId = outputBundle
|
||||
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, 0);
|
||||
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);
|
||||
if (decryptVerifyResult.isKeyPassphraseNeeded()) {
|
||||
// get PendingIntent for passphrase input, add it to given params and return to client
|
||||
Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
|
||||
return passphraseBundle;
|
||||
} else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) {
|
||||
throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
|
||||
}
|
||||
|
||||
int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
|
||||
if (signatureSuccess) {
|
||||
signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED;
|
||||
} else if (signatureUnknown) {
|
||||
signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
|
||||
|
||||
// If signature is unknown we return an additional PendingIntent
|
||||
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
|
||||
if (signatureResult != null) {
|
||||
if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY) {
|
||||
// If signature is unknown we return an _additional_ PendingIntent
|
||||
// to retrieve the missing key
|
||||
// TODO!!!
|
||||
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||
@ -389,11 +320,9 @@ public class OpenPgpService extends RemoteService {
|
||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||
}
|
||||
|
||||
|
||||
OpenPgpSignatureResult sigResult = new OpenPgpSignatureResult(signatureStatus,
|
||||
signatureUserId, signatureOnly, signatureKeyId);
|
||||
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, sigResult);
|
||||
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
|
||||
}
|
||||
|
||||
} finally {
|
||||
is.close();
|
||||
os.close();
|
||||
@ -403,9 +332,44 @@ public class OpenPgpService extends RemoteService {
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Intent result = new Intent();
|
||||
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()));
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -431,7 +395,7 @@ public class OpenPgpService extends RemoteService {
|
||||
if (data == null) {
|
||||
Intent result = new Intent();
|
||||
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);
|
||||
return result;
|
||||
}
|
||||
@ -440,7 +404,7 @@ public class OpenPgpService extends RemoteService {
|
||||
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) {
|
||||
Intent result = new Intent();
|
||||
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);
|
||||
return result;
|
||||
}
|
||||
@ -471,13 +435,12 @@ public class OpenPgpService extends RemoteService {
|
||||
return signImpl(data, input, output, appSettings);
|
||||
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
|
||||
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);
|
||||
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
|
||||
return decryptAndVerifyImpl(data, input, output, appSettings);
|
||||
} else if (OpenPgpApi.ACTION_DOWNLOAD_KEYS.equals(action)) {
|
||||
// TODO!
|
||||
return null;
|
||||
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
|
||||
return getKeyImpl(data);
|
||||
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
|
||||
return getKeyIdsImpl(data);
|
||||
} else {
|
||||
|
@ -72,7 +72,7 @@ public abstract class RemoteService extends Service {
|
||||
// return error
|
||||
Intent result = new Intent();
|
||||
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()));
|
||||
return result;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
|
||||
|
||||
// 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() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -108,7 +108,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
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
|
||||
public void onClick(View v) {
|
||||
// Disallow
|
||||
@ -161,7 +161,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -173,7 +173,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
||||
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
|
||||
public void onClick(View v) {
|
||||
// cancel
|
||||
@ -214,7 +214,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
String text = "<font color=\"red\">" + errorMessage + "</font>";
|
||||
|
||||
// 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() {
|
||||
|
||||
@Override
|
||||
|
@ -230,7 +230,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
|
||||
// Message is received after signing is done in ApgService
|
||||
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) {
|
||||
// handle messages by standard ApgHandler first
|
||||
super.handleMessage(message);
|
||||
@ -249,7 +249,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 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
|
||||
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) {
|
||||
// handle messages by standard ApgHandler first
|
||||
super.handleMessage(message);
|
||||
@ -295,7 +295,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
|
@ -25,6 +25,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Id;
|
||||
@ -32,6 +33,7 @@ import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
@ -528,6 +530,10 @@ public class DecryptActivity extends DrawerActivity {
|
||||
Log.e(Constants.TAG, "File not found!", e);
|
||||
AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
} finally {
|
||||
try {
|
||||
if (inStream != null) inStream.close();
|
||||
} catch (Exception e){ }
|
||||
}
|
||||
} else {
|
||||
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
|
||||
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) {
|
||||
// handle messages by standard ApgHandler first
|
||||
super.handleMessage(message);
|
||||
@ -690,11 +696,15 @@ public class DecryptActivity extends DrawerActivity {
|
||||
|
||||
}
|
||||
|
||||
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE)) {
|
||||
String userId = returnData
|
||||
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
|
||||
mSignatureKeyId = returnData
|
||||
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
|
||||
PgpDecryptVerifyResult decryptVerifyResult =
|
||||
returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
|
||||
|
||||
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
|
||||
|
||||
if (signatureResult != null) {
|
||||
|
||||
String userId = signatureResult.getUserId();
|
||||
mSignatureKeyId = signatureResult.getKeyId();
|
||||
mUserIdRest.setText("id: "
|
||||
+ PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId));
|
||||
if (userId == null) {
|
||||
@ -707,26 +717,37 @@ public class DecryptActivity extends DrawerActivity {
|
||||
}
|
||||
mUserId.setText(userId);
|
||||
|
||||
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS)) {
|
||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
|
||||
mLookupKey.setVisibility(View.GONE);
|
||||
} else if (returnData
|
||||
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) {
|
||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||
mLookupKey.setVisibility(View.VISIBLE);
|
||||
AppMsg.makeText(DecryptActivity.this,
|
||||
R.string.unknown_signature,
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
} else {
|
||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||
mLookupKey.setVisibility(View.GONE);
|
||||
switch (signatureResult.getStatus()) {
|
||||
case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {
|
||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
|
||||
mLookupKey.setVisibility(View.GONE);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO!
|
||||
// case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {
|
||||
// break;
|
||||
// }
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
|
@ -92,12 +92,12 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
private SectionView mUserIdsView;
|
||||
private SectionView mKeysView;
|
||||
|
||||
private String mCurrentPassPhrase = null;
|
||||
private String mCurrentPassphrase = null;
|
||||
private String mNewPassPhrase = null;
|
||||
private String mSavedNewPassPhrase = null;
|
||||
private boolean mIsPassPhraseSet;
|
||||
|
||||
private BootstrapButton mChangePassPhrase;
|
||||
private BootstrapButton mChangePassphrase;
|
||||
|
||||
private CheckBox mNoPassphrase;
|
||||
|
||||
@ -134,14 +134,14 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
* @param intent
|
||||
*/
|
||||
private void handleActionCreateKey(Intent intent) {
|
||||
// Inflate a "Done"/"Cancel" custom action bar
|
||||
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_save,
|
||||
// Inflate a "Save"/"Cancel" custom action bar
|
||||
ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
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
|
||||
public void onClick(View v) {
|
||||
cancelClicked();
|
||||
@ -150,7 +150,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
|
||||
Bundle extras = intent.getExtras();
|
||||
|
||||
mCurrentPassPhrase = "";
|
||||
mCurrentPassphrase = "";
|
||||
|
||||
if (extras != null) {
|
||||
// if userId is given, prefill the fields
|
||||
@ -165,7 +165,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
if (noPassphrase) {
|
||||
// check "no passphrase" checkbox and remove button
|
||||
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
|
||||
Bundle data = new Bundle();
|
||||
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE,
|
||||
mCurrentPassPhrase);
|
||||
mCurrentPassphrase);
|
||||
|
||||
serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
|
||||
// Message is received after generating is done in ApgService
|
||||
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() {
|
||||
@Override
|
||||
@ -224,7 +225,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
|
||||
buildLayout();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
@ -248,8 +249,8 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
* @param intent
|
||||
*/
|
||||
private void handleActionEditKey(Intent intent) {
|
||||
// Inflate a "Done"/"Cancel" custom action bar
|
||||
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_save,
|
||||
// Inflate a "Save"/"Cancel" custom action bar
|
||||
ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -279,9 +280,9 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
|
||||
String passPhrase = PassphraseCacheService.getCachedPassphrase(
|
||||
String passphrase = PassphraseCacheService.getCachedPassphrase(
|
||||
EditKeyActivity.this, masterKeyId);
|
||||
mCurrentPassPhrase = passPhrase;
|
||||
mCurrentPassphrase = passphrase;
|
||||
finallySaveClicked();
|
||||
}
|
||||
}
|
||||
@ -326,8 +327,8 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
cancelClicked();
|
||||
return true;
|
||||
case R.id.menu_key_edit_export_file:
|
||||
mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
|
||||
+ "/secexport.asc");
|
||||
long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
|
||||
mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.path.APP_DIR_FILE_SEC);
|
||||
return true;
|
||||
case R.id.menu_key_edit_delete: {
|
||||
// Message is received after key is deleted
|
||||
@ -371,14 +372,14 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentPassPhrase = "";
|
||||
mCurrentPassphrase = "";
|
||||
|
||||
buildLayout();
|
||||
mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
|
||||
if (!mIsPassPhraseSet) {
|
||||
// check "no passphrase" checkbox and remove button
|
||||
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()
|
||||
int title = -1;
|
||||
if (isPassphraseSet()) {
|
||||
title = R.string.title_change_pass_phrase;
|
||||
title = R.string.title_change_passphrase;
|
||||
} else {
|
||||
title = R.string.title_set_passphrase;
|
||||
}
|
||||
@ -427,7 +428,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
setContentView(R.layout.edit_key_activity);
|
||||
|
||||
// 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);
|
||||
|
||||
// Build layout based on given userIds and keys
|
||||
@ -447,7 +448,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
|
||||
updatePassPhraseButtonText();
|
||||
|
||||
mChangePassPhrase.setOnClickListener(new OnClickListener() {
|
||||
mChangePassphrase.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
showSetPassphraseDialog();
|
||||
}
|
||||
@ -462,10 +463,10 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
// remove passphrase
|
||||
mSavedNewPassPhrase = mNewPassPhrase;
|
||||
mNewPassPhrase = "";
|
||||
mChangePassPhrase.setVisibility(View.GONE);
|
||||
mChangePassphrase.setVisibility(View.GONE);
|
||||
} else {
|
||||
mNewPassPhrase = mSavedNewPassPhrase;
|
||||
mChangePassPhrase.setVisibility(View.VISIBLE);
|
||||
mChangePassphrase.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -504,7 +505,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
if (passphrase == null) {
|
||||
showPassphraseDialog(masterKeyId, masterCanSign);
|
||||
} else {
|
||||
mCurrentPassPhrase = passphrase;
|
||||
mCurrentPassphrase = passphrase;
|
||||
finallySaveClicked();
|
||||
}
|
||||
} catch (PgpGeneralException e) {
|
||||
@ -523,7 +524,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
data.putString(KeychainIntentService.SAVE_KEYRING_CURRENT_PASSPHRASE,
|
||||
mCurrentPassPhrase);
|
||||
mCurrentPassphrase);
|
||||
data.putString(KeychainIntentService.SAVE_KEYRING_NEW_PASSPHRASE, mNewPassPhrase);
|
||||
data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_USER_IDS,
|
||||
getUserIds(mUserIdsView));
|
||||
@ -541,7 +542,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
|
||||
// Message is received after saving is done in ApgService
|
||||
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) {
|
||||
// handle messages by standard ApgHandler first
|
||||
super.handleMessage(message);
|
||||
@ -559,7 +560,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
@ -694,7 +695,7 @@ public class EditKeyActivity extends ActionBarActivity {
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -52,17 +52,21 @@ import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ViewFlipper;
|
||||
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
import com.beardedhen.androidbootstrap.FontAwesomeText;
|
||||
import com.devspark.appmsg.AppMsg;
|
||||
|
||||
public class EncryptActivity extends DrawerActivity {
|
||||
@ -101,18 +105,24 @@ public class EncryptActivity extends DrawerActivity {
|
||||
|
||||
private int mEncryptTarget;
|
||||
|
||||
private EditText mPassPhrase = null;
|
||||
private EditText mPassPhraseAgain = null;
|
||||
private EditText mPassphrase = null;
|
||||
private EditText mPassphraseAgain = null;
|
||||
private CheckBox mAsciiArmor = null;
|
||||
private Spinner mFileCompression = null;
|
||||
|
||||
private EditText mFilename = null;
|
||||
private CheckBox mDeleteAfter = null;
|
||||
private CheckBox mShareAfter = null;
|
||||
private BootstrapButton mBrowse = null;
|
||||
|
||||
private String mInputFilename = 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 mOverrideAsciiArmor = false;
|
||||
|
||||
@ -147,6 +157,9 @@ public class EncryptActivity extends DrawerActivity {
|
||||
updateMode();
|
||||
|
||||
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
|
||||
if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
|
||||
boolean gotPassPhrase = false;
|
||||
String passPhrase = mPassPhrase.getText().toString();
|
||||
String passPhraseAgain = mPassPhraseAgain.getText().toString();
|
||||
if (!passPhrase.equals(passPhraseAgain)) {
|
||||
String passphrase = mPassphrase.getText().toString();
|
||||
String passphraseAgain = mPassphraseAgain.getText().toString();
|
||||
if (!passphrase.equals(passphraseAgain)) {
|
||||
AppMsg.makeText(this, R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
gotPassPhrase = (passPhrase.length() != 0);
|
||||
gotPassPhrase = (passphrase.length() != 0);
|
||||
if (!gotPassPhrase) {
|
||||
AppMsg.makeText(this, R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
|
||||
.show();
|
||||
@ -550,11 +563,11 @@ public class EncryptActivity extends DrawerActivity {
|
||||
|
||||
if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
|
||||
Log.d(Constants.TAG, "Symmetric encryption enabled!");
|
||||
String passPhrase = mPassPhrase.getText().toString();
|
||||
if (passPhrase.length() == 0) {
|
||||
passPhrase = null;
|
||||
String passphrase = mPassphrase.getText().toString();
|
||||
if (passphrase.length() == 0) {
|
||||
passphrase = null;
|
||||
}
|
||||
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passPhrase);
|
||||
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase);
|
||||
} else {
|
||||
mSecretKeyIdToPass = mSecretKeyId;
|
||||
encryptionKeyIds = mEncryptionKeyIds;
|
||||
@ -604,7 +617,7 @@ public class EncryptActivity extends DrawerActivity {
|
||||
|
||||
// Message is received after encrypting is done in ApgService
|
||||
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) {
|
||||
// handle messages by standard ApgHandler first
|
||||
super.handleMessage(message);
|
||||
@ -650,6 +663,15 @@ public class EncryptActivity extends DrawerActivity {
|
||||
.newInstance(mInputFilename);
|
||||
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;
|
||||
|
||||
default:
|
||||
@ -659,8 +681,6 @@ public class EncryptActivity extends DrawerActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
@ -766,8 +786,8 @@ public class EncryptActivity extends DrawerActivity {
|
||||
mMainUserId = (TextView) findViewById(R.id.mainUserId);
|
||||
mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
|
||||
|
||||
mPassPhrase = (EditText) findViewById(R.id.passPhrase);
|
||||
mPassPhraseAgain = (EditText) findViewById(R.id.passPhraseAgain);
|
||||
mPassphrase = (EditText) findViewById(R.id.passphrase);
|
||||
mPassphraseAgain = (EditText) findViewById(R.id.passphraseAgain);
|
||||
|
||||
// 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.
|
||||
@ -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);
|
||||
Choice[] choices = new Choice[]{
|
||||
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);
|
||||
mShareAfter = (CheckBox) findViewById(R.id.shareAfterEncryption);
|
||||
|
||||
mAsciiArmor = (CheckBox) findViewById(R.id.asciiArmour);
|
||||
mAsciiArmor.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour());
|
||||
|
@ -22,16 +22,11 @@ import java.util.ArrayList;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
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.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class HelpActivity extends ActionBarActivity {
|
||||
public static final String EXTRA_SELECTED_TAB = "selectedTab";
|
||||
@ -64,19 +59,19 @@ public class HelpActivity extends ActionBarActivity {
|
||||
Bundle startBundle = new Bundle();
|
||||
startBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_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();
|
||||
nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_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();
|
||||
changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_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)),
|
||||
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)) {
|
||||
long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
|
||||
if (keyId != 0) {
|
||||
query = "0x" + PgpKeyHelper.convertKeyToHex(keyId);
|
||||
query = PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||
}
|
||||
} else if (extras.containsKey(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
|
||||
*/
|
||||
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) {
|
||||
Log.d(Constants.TAG, "importKeys started");
|
||||
|
||||
|
@ -219,27 +219,44 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
} else {
|
||||
setListShownNoAnimation(true);
|
||||
}
|
||||
|
||||
Exception error = data.getError();
|
||||
|
||||
switch (loader.getId()) {
|
||||
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;
|
||||
|
||||
case LOADER_ID_SERVER_QUERY:
|
||||
|
||||
Exception error = data.getError();
|
||||
|
||||
if(error == null){
|
||||
if(error == null) {
|
||||
AppMsg.makeText(
|
||||
getActivity(), getResources().getQuantityString(R.plurals.keys_found,
|
||||
mAdapter.getCount(), mAdapter.getCount()),
|
||||
AppMsg.STYLE_INFO
|
||||
).show();
|
||||
} else if(error instanceof KeyServer.InsufficientQuery){
|
||||
} else if(error instanceof KeyServer.InsufficientQuery) {
|
||||
AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query,
|
||||
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.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.STYLE_ALERT).show();
|
||||
}
|
||||
|
@ -59,8 +59,8 @@ public class KeyListActivity extends DrawerActivity {
|
||||
|
||||
return true;
|
||||
case R.id.menu_key_list_export:
|
||||
mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR
|
||||
+ "/pubexport.asc");
|
||||
// TODO fix this for unified keylist
|
||||
mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
|
||||
|
||||
return true;
|
||||
default:
|
||||
|
@ -19,11 +19,14 @@ package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Id;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.helper.ExportHelper;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
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.Loader;
|
||||
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.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.AbsListView.MultiChoiceModeListener;
|
||||
import android.widget.AdapterView;
|
||||
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
|
||||
* 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> {
|
||||
|
||||
private KeyListAdapter mAdapter;
|
||||
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
|
||||
private BootstrapButton mButtonEmptyCreate;
|
||||
private BootstrapButton mButtonEmptyImport;
|
||||
|
||||
|
||||
/**
|
||||
* Load custom layout with StickyListView from library
|
||||
*/
|
||||
@Override
|
||||
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() {
|
||||
|
||||
@Override
|
||||
@ -102,8 +126,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
});
|
||||
|
||||
mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import);
|
||||
mButtonEmptyImport = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_import);
|
||||
mButtonEmptyImport.setOnClickListener(new OnClickListener() {
|
||||
|
||||
@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) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
|
||||
|
||||
mStickyList.setOnItemClickListener(this);
|
||||
mStickyList.setAreHeadersSticky(true);
|
||||
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
|
||||
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
|
||||
@ -147,8 +173,6 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
||||
mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
|
||||
|
||||
private int count = 0;
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
android.view.MenuInflater inflater = getActivity().getMenuInflater();
|
||||
@ -174,7 +198,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
||||
break;
|
||||
}
|
||||
case R.id.menu_key_list_multi_delete: {
|
||||
ids = mAdapter.getCurrentSelectedItemIds();
|
||||
ids = mStickyList.getWrappedList().getCheckedItemIds();
|
||||
showDeleteKeyDialog(mode, ids);
|
||||
break;
|
||||
}
|
||||
@ -184,7 +208,6 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
count = 0;
|
||||
mAdapter.clearSelection();
|
||||
}
|
||||
|
||||
@ -192,13 +215,11 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
|
||||
boolean checked) {
|
||||
if (checked) {
|
||||
count++;
|
||||
mAdapter.setNewSelection(position, checked);
|
||||
} else {
|
||||
count--;
|
||||
mAdapter.removeSelection(position);
|
||||
}
|
||||
|
||||
int count = mStickyList.getCheckedItemCount();
|
||||
String keysSelected = getResources().getQuantityString(
|
||||
R.plurals.key_list_selected_keys, count, count);
|
||||
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.
|
||||
// setListShown(false);
|
||||
setListShown(false);
|
||||
|
||||
// Create an empty adapter we will use to display the loaded data.
|
||||
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
|
||||
// sample only has one Loader, so we don't care about the ID.
|
||||
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
|
||||
// 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
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||
// Swap the new cursor in. (The framework will take care of closing the
|
||||
// old cursor once we return.)
|
||||
mAdapter.setSearchQuery(mCurQuery);
|
||||
mAdapter.swapCursor(data);
|
||||
|
||||
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.
|
||||
// if (isResumed()) {
|
||||
// setListShown(true);
|
||||
// } else {
|
||||
// setListShownNoAnimation(true);
|
||||
// }
|
||||
if (isResumed()) {
|
||||
setListShown(true);
|
||||
} else {
|
||||
setListShownNoAnimation(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -338,6 +368,87 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
||||
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
|
||||
*/
|
||||
@ -347,6 +458,8 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
||||
private int mIndexIsRevoked;
|
||||
private int mMasterKeyId;
|
||||
|
||||
private String mCurQuery;
|
||||
|
||||
@SuppressLint("UseSparseArrays")
|
||||
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[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
||||
if (userIdSplit[0] != null) {
|
||||
mainUserId.setText(userIdSplit[0]);
|
||||
mainUserId.setText(highlightSearchQuery(userIdSplit[0]));
|
||||
} else {
|
||||
mainUserId.setText(R.string.user_id_no_name);
|
||||
}
|
||||
if (userIdSplit[1] != null) {
|
||||
mainUserIdRest.setText(userIdSplit[1]);
|
||||
mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1]));
|
||||
mainUserIdRest.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mainUserIdRest.setVisibility(View.GONE);
|
||||
@ -558,20 +671,6 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
||||
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() {
|
||||
long[] ids = new long[mSelection.size()];
|
||||
int i = 0;
|
||||
@ -608,6 +707,34 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick
|
||||
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.ui.widget.IntegerListPreference;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public class PreferencesActivity extends PreferenceActivity {
|
||||
private IntegerListPreference mPassPhraseCacheTtl = null;
|
||||
private IntegerListPreference mEncryptionAlgorithm = null;
|
||||
private IntegerListPreference mHashAlgorithm = null;
|
||||
private IntegerListPreference mMessageCompression = null;
|
||||
private IntegerListPreference mFileCompression = null;
|
||||
private CheckBoxPreference mAsciiArmour = null;
|
||||
private CheckBoxPreference mForceV3Signatures = null;
|
||||
|
||||
public final static String ACTION_PREFS_GEN = "org.sufficientlysecure.keychain.ui.PREFS_GEN";
|
||||
public final static String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
|
||||
|
||||
private PreferenceScreen mKeyServerPreference = null;
|
||||
private Preferences mPreferences;
|
||||
private static Preferences mPreferences;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -53,22 +56,218 @@ public class PreferencesActivity extends PreferenceActivity {
|
||||
// actionBar.setDisplayHomeAsUpEnabled(false);
|
||||
// actionBar.setHomeButtonEnabled(false);
|
||||
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
String action = getIntent().getAction();
|
||||
|
||||
mPassPhraseCacheTtl = (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL);
|
||||
mPassPhraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl());
|
||||
mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
|
||||
mPassPhraseCacheTtl
|
||||
if (action != null && action.equals(ACTION_PREFS_GEN)) {
|
||||
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(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() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
mPassPhraseCacheTtl.setValue(newValue.toString());
|
||||
mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
|
||||
mPassphraseCacheTtl.setValue(newValue.toString());
|
||||
mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
|
||||
mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
|
||||
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,
|
||||
PGPEncryptedData.AES_256, PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH,
|
||||
PGPEncryptedData.CAST5, PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES,
|
||||
@ -93,8 +292,10 @@ public class PreferencesActivity extends PreferenceActivity {
|
||||
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,
|
||||
HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256,
|
||||
HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512, };
|
||||
@ -116,19 +317,10 @@ public class PreferencesActivity extends PreferenceActivity {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mMessageCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION);
|
||||
valueIds = new int[] { Id.choice.compression.none, Id.choice.compression.zip,
|
||||
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];
|
||||
}
|
||||
private static void initializeMessageCompression
|
||||
(final IntegerListPreference mMessageCompression, int[] valueIds, String[] entries, String[] values) {
|
||||
mMessageCompression.setEntries(entries);
|
||||
mMessageCompression.setEntryValues(values);
|
||||
mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
|
||||
@ -143,8 +335,10 @@ public class PreferencesActivity extends PreferenceActivity {
|
||||
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.setEntryValues(values);
|
||||
mFileCompression.setValue("" + mPreferences.getDefaultFileCompression());
|
||||
@ -157,8 +351,9 @@ public class PreferencesActivity extends PreferenceActivity {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mAsciiArmour = (CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR);
|
||||
private static void initializeAsciiArmour(final CheckBoxPreference mAsciiArmour) {
|
||||
mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour());
|
||||
mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
@ -167,8 +362,9 @@ public class PreferencesActivity extends PreferenceActivity {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mForceV3Signatures = (CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES);
|
||||
private static void initializeForceV3Signatures(final CheckBoxPreference mForceV3Signatures) {
|
||||
mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures());
|
||||
mForceV3Signatures
|
||||
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@ -178,43 +374,5 @@ public class PreferencesActivity extends PreferenceActivity {
|
||||
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);
|
||||
|
||||
// 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() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// ok
|
||||
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
|
||||
public void onClick(View v) {
|
||||
// cancel
|
||||
@ -81,11 +81,11 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
|
||||
Intent intent = getIntent();
|
||||
String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS);
|
||||
if (servers != null) {
|
||||
for (int i = 0; i < servers.length; ++i) {
|
||||
for (String serv : servers) {
|
||||
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(
|
||||
R.layout.key_server_editor, mEditors, false);
|
||||
view.setEditorListener(this);
|
||||
view.setValue(servers[i]);
|
||||
view.setValue(serv);
|
||||
mEditors.addView(view);
|
||||
}
|
||||
}
|
||||
|
@ -46,14 +46,14 @@ public class SelectPublicKeyActivity extends ActionBarActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// 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() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// ok
|
||||
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
|
||||
public void onClick(View v) {
|
||||
// cancel
|
||||
|
@ -30,7 +30,7 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.Uri;
|
||||
@ -38,17 +38,35 @@ import android.os.Bundle;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
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.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
public class SelectPublicKeyFragment extends ListFragmentWorkaround implements TextWatcher,
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
public static final String ARG_PRESELECTED_KEY_IDS = "preselected_key_ids";
|
||||
|
||||
private Activity mActivity;
|
||||
private SelectKeyCursorAdapter mAdapter;
|
||||
private ListView mListView;
|
||||
|
||||
private EditText mSearchView;
|
||||
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
|
||||
@ -67,10 +85,84 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
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
|
||||
*/
|
||||
@ -78,16 +170,15 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
mActivity = getActivity();
|
||||
mListView = getListView();
|
||||
|
||||
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
|
||||
// Give some text to display if there is no data. In a real
|
||||
// application this would come from a resource.
|
||||
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);
|
||||
|
||||
@ -106,11 +197,11 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
*/
|
||||
private void preselectMasterKeyIds(long[] masterKeyIds) {
|
||||
if (masterKeyIds != null) {
|
||||
for (int i = 0; i < mListView.getCount(); ++i) {
|
||||
for (int i = 0; i < getListView().getCount(); ++i) {
|
||||
long keyId = mAdapter.getMasterKeyId(i);
|
||||
for (int j = 0; j < masterKeyIds.length; ++j) {
|
||||
if (keyId == masterKeyIds[j]) {
|
||||
mListView.setItemChecked(i, true);
|
||||
for (long masterKeyId : masterKeyIds) {
|
||||
if (keyId == masterKeyId) {
|
||||
getListView().setItemChecked(i, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -127,8 +218,8 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
// mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key
|
||||
// ids!
|
||||
Vector<Long> vector = new Vector<Long>();
|
||||
for (int i = 0; i < mListView.getCount(); ++i) {
|
||||
if (mListView.isItemChecked(i)) {
|
||||
for (int i = 0; i < getListView().getCount(); ++i) {
|
||||
if (getListView().isItemChecked(i)) {
|
||||
vector.add(mAdapter.getMasterKeyId(i));
|
||||
}
|
||||
}
|
||||
@ -149,8 +240,8 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
*/
|
||||
public String[] getSelectedUserIds() {
|
||||
Vector<String> userIds = new Vector<String>();
|
||||
for (int i = 0; i < mListView.getCount(); ++i) {
|
||||
if (mListView.isItemChecked(i)) {
|
||||
for (int i = 0; i < getListView().getCount(); ++i) {
|
||||
if (getListView().isItemChecked(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.
|
||||
long now = new Date().getTime() / 1000;
|
||||
String[] projection = new String[] {
|
||||
String[] projection = new String[]{
|
||||
KeyRings._ID,
|
||||
KeyRings.MASTER_KEY_ID,
|
||||
UserIds.USER_ID,
|
||||
@ -185,7 +276,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
+ Keys.CAN_ENCRYPT + " = '1' AND valid_keys." + Keys.CREATION + " <= '"
|
||||
+ now + "' AND " + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys."
|
||||
+ Keys.EXPIRY + " >= '" + now + "')) AS "
|
||||
+ SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
|
||||
+ SelectKeyCursorAdapter.PROJECTION_ROW_VALID,};
|
||||
|
||||
String inMasterKeyList = null;
|
||||
if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) {
|
||||
@ -199,37 +290,28 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
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";
|
||||
if (inMasterKeyList != null) {
|
||||
// sort by selected master keys
|
||||
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
|
||||
// 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
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||
// Swap the new cursor in. (The framework will take care of closing the
|
||||
// old cursor once we return.)
|
||||
mAdapter.setSearchQuery(mCurQuery);
|
||||
mAdapter.swapCursor(data);
|
||||
|
||||
// The list should now be shown.
|
||||
@ -250,4 +332,20 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
// longer using it.
|
||||
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.provider.ProviderHelper;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
@ -40,6 +43,7 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
|
||||
|
||||
private TextView mKeyUserId;
|
||||
private TextView mKeyUserIdRest;
|
||||
private TextView mKeyMasterKeyIdHex;
|
||||
private BootstrapButton mSelectKeyButton;
|
||||
private Boolean mFilterCertify;
|
||||
|
||||
@ -61,26 +65,52 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
|
||||
|
||||
public void selectKey(long secretKeyId) {
|
||||
if (secretKeyId == Id.key.none) {
|
||||
mKeyUserId.setText(R.string.api_settings_no_key);
|
||||
mKeyMasterKeyIdHex.setText(R.string.api_settings_no_key);
|
||||
mKeyUserIdRest.setText("");
|
||||
mKeyUserId.setVisibility(View.GONE);
|
||||
mKeyUserIdRest.setVisibility(View.GONE);
|
||||
|
||||
} else {
|
||||
String uid = getResources().getString(R.string.user_id_no_name);
|
||||
String uidExtra = "";
|
||||
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
|
||||
getActivity(), secretKeyId);
|
||||
if (keyRing != null) {
|
||||
PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
|
||||
String masterkeyIdHex = PgpKeyHelper.convertKeyIdToHex(secretKeyId);
|
||||
|
||||
if (key != null) {
|
||||
String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key);
|
||||
String chunks[] = userId.split(" <", 2);
|
||||
uid = chunks[0];
|
||||
if (chunks.length > 1) {
|
||||
uidExtra = "<" + chunks[1];
|
||||
|
||||
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
||||
String userName, userEmail;
|
||||
|
||||
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);
|
||||
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
|
||||
.findViewById(R.id.select_secret_key_select_key_button);
|
||||
mFilterCertify = false;
|
||||
@ -117,30 +148,31 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
|
||||
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
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode & 0xFFFF) {
|
||||
case REQUEST_CODE_SELECT_KEY: {
|
||||
long secretKeyId;
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
Bundle bundle = data.getExtras();
|
||||
secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
|
||||
case REQUEST_CODE_SELECT_KEY: {
|
||||
long secretKeyId;
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
Bundle bundle = data.getExtras();
|
||||
secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
|
||||
selectKey(secretKeyId);
|
||||
|
||||
selectKey(secretKeyId);
|
||||
// remove displayed errors
|
||||
mKeyUserId.setError(null);
|
||||
|
||||
// remove displayed errors
|
||||
mKeyUserId.setError(null);
|
||||
|
||||
// give value back to callback
|
||||
mCallback.onKeySelected(secretKeyId);
|
||||
// give value back to callback
|
||||
mCallback.onKeySelected(secretKeyId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
default:
|
||||
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
|
||||
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) {
|
||||
// handle messages by standard ApgHandler first
|
||||
super.handleMessage(message);
|
||||
@ -113,7 +113,7 @@ public class UploadKeyActivity extends ActionBarActivity {
|
||||
Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
|
@ -93,12 +93,12 @@ public class ViewKeyActivity extends ActionBarActivity {
|
||||
Bundle mainBundle = new Bundle();
|
||||
mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
|
||||
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();
|
||||
certBundle.putLong(ViewKeyCertsFragment.ARG_KEYRING_ROW_ID, rowId);
|
||||
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
|
||||
@ -123,8 +123,8 @@ public class ViewKeyActivity extends ActionBarActivity {
|
||||
uploadToKeyserver(mDataUri);
|
||||
return true;
|
||||
case R.id.menu_key_view_export_file:
|
||||
mExportHelper.showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
|
||||
+ "/pubexport.asc");
|
||||
long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
|
||||
mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB);
|
||||
return true;
|
||||
case R.id.menu_key_view_share_default_fingerprint:
|
||||
shareKey(mDataUri, true);
|
||||
@ -159,7 +159,7 @@ public class ViewKeyActivity extends ActionBarActivity {
|
||||
}
|
||||
|
||||
private void updateFromKeyserver(Uri dataUri) {
|
||||
long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, mDataUri);
|
||||
long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, dataUri);
|
||||
|
||||
if (updateKeyId == 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.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -273,7 +276,7 @@ public class ViewKeyMainFragment extends Fragment implements
|
||||
// get key id from MASTER_KEY_ID
|
||||
long keyId = data.getLong(KEYS_INDEX_KEY_ID);
|
||||
|
||||
String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||
mKeyId.setText(keyIdStr);
|
||||
|
||||
// get creation date from CREATION
|
||||
@ -306,9 +309,8 @@ public class ViewKeyMainFragment extends Fragment implements
|
||||
fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri);
|
||||
}
|
||||
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);
|
||||
fingerprint = fingerprint.replace(" ", "\n");
|
||||
|
||||
mFingerprint.setText(fingerprint);
|
||||
mFingerprint.setText(colorizeFingerprint(fingerprint));
|
||||
}
|
||||
|
||||
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.
|
||||
* 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 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) {
|
||||
super(activity, -1);
|
||||
mActivity = activity;
|
||||
@ -86,16 +94,21 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ImportKeysListEntry entry = data.get(position);
|
||||
|
||||
View view = mInflater.inflate(R.layout.import_keys_list_entry, null);
|
||||
|
||||
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
|
||||
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
||||
TextView keyId = (TextView) view.findViewById(R.id.keyId);
|
||||
TextView fingerprint = (TextView) view.findViewById(R.id.fingerprint);
|
||||
TextView algorithm = (TextView) view.findViewById(R.id.algorithm);
|
||||
TextView status = (TextView) view.findViewById(R.id.status);
|
||||
|
||||
ViewHolder holder;
|
||||
if(convertView == null) {
|
||||
holder = new ViewHolder();
|
||||
convertView = mInflater.inflate(R.layout.import_keys_list_entry, null);
|
||||
holder.mainUserId = (TextView) convertView.findViewById(R.id.mainUserId);
|
||||
holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest);
|
||||
holder.keyId = (TextView) convertView.findViewById(R.id.keyId);
|
||||
holder.fingerprint = (TextView) convertView.findViewById(R.id.fingerprint);
|
||||
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
|
||||
String userId = entry.userIds.get(0);
|
||||
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
|
||||
if (entry.secretKey) {
|
||||
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 {
|
||||
mainUserId.setText(R.string.user_id_no_name);
|
||||
holder.mainUserId.setText(R.string.user_id_no_name);
|
||||
}
|
||||
|
||||
// email
|
||||
if (userIdSplit[1] != null) {
|
||||
mainUserIdRest.setText(userIdSplit[1]);
|
||||
mainUserIdRest.setVisibility(View.VISIBLE);
|
||||
holder.mainUserIdRest.setText(userIdSplit[1]);
|
||||
holder.mainUserIdRest.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mainUserIdRest.setVisibility(View.GONE);
|
||||
holder.mainUserIdRest.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
keyId.setText(entry.hexKeyId);
|
||||
holder.keyId.setText(entry.hexKeyId);
|
||||
|
||||
if (entry.fingerPrint != null) {
|
||||
fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint);
|
||||
fingerprint.setVisibility(View.VISIBLE);
|
||||
holder.fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint);
|
||||
holder.fingerprint.setVisibility(View.VISIBLE);
|
||||
} 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) {
|
||||
status.setText(R.string.revoked);
|
||||
holder.status.setText(R.string.revoked);
|
||||
} 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) {
|
||||
ll.setVisibility(View.GONE);
|
||||
} 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());
|
||||
|
||||
return view;
|
||||
return convertView;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -125,7 +125,10 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
||||
* Constructor for later querying from keyserver
|
||||
*/
|
||||
public ImportKeysListEntry() {
|
||||
// keys from keyserver are always public keys
|
||||
secretKey = false;
|
||||
// do not select by default
|
||||
selected = false;
|
||||
userIds = new ArrayList<String>();
|
||||
}
|
||||
|
||||
@ -167,7 +170,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
||||
this.revoked = pgpKeyRing.getPublicKey().isRevoked();
|
||||
this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
|
||||
.getFingerprint(), true);
|
||||
this.hexKeyId = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||
this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||
this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength();
|
||||
int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
|
||||
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.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
@ -34,6 +33,21 @@ import android.content.Context;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
|
||||
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;
|
||||
|
||||
InputData mInputData;
|
||||
@ -88,21 +102,26 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper
|
||||
/**
|
||||
* Reads all PGPKeyRing objects from input
|
||||
*
|
||||
* @param keyringBytes
|
||||
* @param inputData
|
||||
* @return
|
||||
*/
|
||||
private void generateListOfKeyrings(InputData inputData) {
|
||||
|
||||
boolean isEmpty = true;
|
||||
int nonPgpCounter = 0;
|
||||
|
||||
PositionAwareInputStream progressIn = new PositionAwareInputStream(
|
||||
inputData.getInputStream());
|
||||
|
||||
// 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
|
||||
// armour blocks
|
||||
// armor blocks
|
||||
BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
|
||||
try {
|
||||
|
||||
// read all available blocks... (asc files can contain many blocks with BEGIN END)
|
||||
while (bufferedInput.available() > 0) {
|
||||
isEmpty = false;
|
||||
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
|
||||
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
|
||||
|
||||
@ -116,11 +135,25 @@ public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper
|
||||
addToData(newKeyring);
|
||||
} else {
|
||||
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
|
||||
nonPgpCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception 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.database.Cursor;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -33,7 +32,9 @@ import android.widget.CheckBox;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class SelectKeyCursorAdapter extends CursorAdapter {
|
||||
|
||||
|
||||
public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter {
|
||||
|
||||
protected int mKeyType;
|
||||
|
||||
@ -55,7 +56,6 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mListView = listView;
|
||||
mKeyType = keyType;
|
||||
|
||||
initIndex(c);
|
||||
}
|
||||
|
||||
@ -104,12 +104,12 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
|
||||
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
|
||||
|
||||
if (userIdSplit[0] != null) {
|
||||
mainUserId.setText(userIdSplit[0]);
|
||||
mainUserId.setText(highlightSearchQuery(userIdSplit[0]));
|
||||
} else {
|
||||
mainUserId.setText(R.string.user_id_no_name);
|
||||
}
|
||||
if (userIdSplit[1] != null) {
|
||||
mainUserIdRest.setText(userIdSplit[1]);
|
||||
mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1]));
|
||||
} else {
|
||||
mainUserIdRest.setText("");
|
||||
}
|
||||
@ -164,5 +164,4 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
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 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),
|
||||
cursor.getInt(mIndexKeySize));
|
||||
|
||||
|
@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.Id;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Choice;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Vector;
|
||||
|
||||
public class CreateKeyDialogFragment extends DialogFragment {
|
||||
@ -78,7 +79,7 @@ public class CreateKeyDialogFragment extends DialogFragment {
|
||||
boolean wouldBeMasterKey = (childCount == 0);
|
||||
|
||||
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(
|
||||
R.string.dsa)));
|
||||
if (!wouldBeMasterKey) {
|
||||
|
@ -83,7 +83,10 @@ public class DeleteFileDialogFragment extends DialogFragment {
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
|
||||
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
|
||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(activity, deletingDialog) {
|
||||
@ -95,7 +98,7 @@ public class DeleteFileDialogFragment extends DialogFragment {
|
||||
Toast.makeText(activity, R.string.file_delete_successful,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
|
@ -33,7 +33,6 @@ import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
|
@ -153,17 +153,17 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
||||
dismiss();
|
||||
long curKeyIndex = 1;
|
||||
boolean keyOK = true;
|
||||
String passPhrase = mPassphraseEditText.getText().toString();
|
||||
String passphrase = mPassphraseEditText.getText().toString();
|
||||
long keyId;
|
||||
PGPSecretKey clickSecretKey = secretKey;
|
||||
|
||||
if (clickSecretKey != null) {
|
||||
while (keyOK == true) {
|
||||
while (keyOK) {
|
||||
if (clickSecretKey != null) { // check again for loop
|
||||
try {
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||
passPhrase.toCharArray());
|
||||
passphrase.toCharArray());
|
||||
PGPPrivateKey testKey = clickSecretKey
|
||||
.extractPrivateKey(keyDecryptor);
|
||||
if (testKey == null) {
|
||||
@ -206,10 +206,10 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
||||
|
||||
// cache the new passphrase
|
||||
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
|
||||
PassphraseCacheService.addCachedPassphrase(activity, keyId, passPhrase);
|
||||
if (keyOK == false && clickSecretKey.getKeyID() != keyId) {
|
||||
PassphraseCacheService.addCachedPassphrase(activity, keyId, passphrase);
|
||||
if ( !keyOK && clickSecretKey.getKeyID() != keyId) {
|
||||
PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(),
|
||||
passPhrase);
|
||||
passphrase);
|
||||
}
|
||||
|
||||
sendMessageToHandler(MESSAGE_OKAY);
|
||||
|
@ -30,7 +30,7 @@ import android.view.KeyEvent;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
|
||||
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_CANCELABLE = "cancelable";
|
||||
|
||||
@ -39,16 +39,16 @@ public class ProgressDialogFragment extends DialogFragment {
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*
|
||||
* @param messageId
|
||||
* @param message
|
||||
* @param style
|
||||
* @param cancelable
|
||||
* @return
|
||||
*/
|
||||
public static ProgressDialogFragment newInstance(int messageId, int style, boolean cancelable,
|
||||
public static ProgressDialogFragment newInstance(String message, int style, boolean cancelable,
|
||||
OnCancelListener onCancelListener) {
|
||||
ProgressDialogFragment frag = new ProgressDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_MESSAGE_ID, messageId);
|
||||
args.putString(ARG_MESSAGE, message);
|
||||
args.putInt(ARG_STYLE, style);
|
||||
args.putBoolean(ARG_CANCELABLE, cancelable);
|
||||
|
||||
@ -117,22 +117,22 @@ public class ProgressDialogFragment extends DialogFragment {
|
||||
dialog.setCancelable(false);
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
|
||||
int messageId = getArguments().getInt(ARG_MESSAGE_ID);
|
||||
String message = getArguments().getString(ARG_MESSAGE);
|
||||
int style = getArguments().getInt(ARG_STYLE);
|
||||
boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE);
|
||||
|
||||
dialog.setMessage(getString(messageId));
|
||||
dialog.setMessage(message);
|
||||
dialog.setProgressStyle(style);
|
||||
|
||||
if (cancelable) {
|
||||
dialog.setButton(DialogInterface.BUTTON_NEGATIVE,
|
||||
activity.getString(R.string.progress_cancel),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Disable the back button
|
||||
@ -140,7 +140,6 @@ public class ProgressDialogFragment extends DialogFragment {
|
||||
|
||||
@Override
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return true;
|
||||
}
|
||||
|
@ -101,9 +101,9 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dismiss();
|
||||
|
||||
String passPhrase1 = mPassphraseEditText.getText().toString();
|
||||
String passPhrase2 = mPassphraseAgainEditText.getText().toString();
|
||||
if (!passPhrase1.equals(passPhrase2)) {
|
||||
String passphrase1 = mPassphraseEditText.getText().toString();
|
||||
String passphrase2 = mPassphraseAgainEditText.getText().toString();
|
||||
if (!passphrase1.equals(passphrase2)) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.error_message,
|
||||
@ -112,7 +112,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
return;
|
||||
}
|
||||
|
||||
if (passPhrase1.equals("")) {
|
||||
if (passphrase1.equals("")) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.error_message,
|
||||
@ -123,7 +123,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
|
||||
// return resulting data back to activity
|
||||
Bundle data = new Bundle();
|
||||
data.putString(MESSAGE_NEW_PASSPHRASE, passPhrase1);
|
||||
data.putString(MESSAGE_NEW_PASSPHRASE, passphrase1);
|
||||
|
||||
sendMessageToHandler(MESSAGE_OKAY, data);
|
||||
}
|
||||
|
@ -53,7 +53,6 @@ public class ShareNfcDialogFragment extends DialogFragment {
|
||||
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
||||
|
||||
alert.setIcon(android.R.drawable.ic_dialog_info);
|
||||
alert.setTitle(R.string.share_nfc_dialog);
|
||||
alert.setCancelable(true);
|
||||
|
||||
|
@ -34,6 +34,7 @@ import android.app.DatePickerDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
@ -58,6 +59,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
||||
Spinner mUsage;
|
||||
TextView mCreationDate;
|
||||
BootstrapButton mExpiryDateButton;
|
||||
GregorianCalendar mCreatedDate;
|
||||
GregorianCalendar mExpiryDate;
|
||||
|
||||
private int mDatePickerResultCount = 0;
|
||||
@ -113,8 +115,12 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
||||
if (date == null) {
|
||||
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),
|
||||
date.get(Calendar.DAY_OF_MONTH));
|
||||
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();
|
||||
}
|
||||
});
|
||||
@ -153,9 +174,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
||||
}
|
||||
|
||||
mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(key));
|
||||
String keyId1Str = PgpKeyHelper.convertKeyIdToHex(key.getKeyID());
|
||||
String keyId2Str = PgpKeyHelper.convertKeyIdToHex(key.getKeyID() >> 32);
|
||||
mKeyId.setText(keyId1Str + " " + keyId2Str);
|
||||
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyID());
|
||||
mKeyId.setText(keyIdStr);
|
||||
|
||||
Vector<Choice> choices = new Vector<Choice>();
|
||||
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"));
|
||||
cal.setTime(PgpKeyHelper.getCreationDate(key));
|
||||
mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime()));
|
||||
setCreatedDate(cal);
|
||||
cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
|
||||
if (expiryDate == null) {
|
||||
@ -235,6 +255,15 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
||||
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) {
|
||||
mExpiryDate = date;
|
||||
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) {
|
||||
mType = type;
|
||||
switch (type) {
|
||||
case Id.type.user_id: {
|
||||
mTitle.setText(R.string.section_user_ids);
|
||||
break;
|
||||
}
|
||||
case Id.type.user_id: {
|
||||
mTitle.setText(R.string.section_user_ids);
|
||||
break;
|
||||
}
|
||||
|
||||
case Id.type.key: {
|
||||
mTitle.setText(R.string.section_keys);
|
||||
break;
|
||||
}
|
||||
case Id.type.key: {
|
||||
mTitle.setText(R.string.section_keys);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,7 +103,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
@ -121,7 +123,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
||||
super.onFinishInflate();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void onDeleted(Editor editor) {
|
||||
this.updateEditorsVisible();
|
||||
}
|
||||
@ -131,38 +135,40 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
||||
mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void onClick(View v) {
|
||||
if (canEdit) {
|
||||
switch (mType) {
|
||||
case Id.type.user_id: {
|
||||
UserIdEditor view = (UserIdEditor) mInflater.inflate(
|
||||
R.layout.edit_key_user_id_item, mEditors, false);
|
||||
view.setEditorListener(this);
|
||||
if (mEditors.getChildCount() == 0) {
|
||||
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();
|
||||
case Id.type.user_id: {
|
||||
UserIdEditor view = (UserIdEditor) mInflater.inflate(
|
||||
R.layout.edit_key_user_id_item, mEditors, false);
|
||||
view.setEditorListener(this);
|
||||
if (mEditors.getChildCount() == 0) {
|
||||
view.setIsMainUserId(true);
|
||||
}
|
||||
});
|
||||
mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog");
|
||||
break;
|
||||
}
|
||||
mEditors.addView(view);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
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();
|
||||
}
|
||||
});
|
||||
mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog");
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.updateEditorsVisible();
|
||||
}
|
||||
@ -220,31 +226,34 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
||||
Bundle data = new Bundle();
|
||||
Boolean isMasterKey;
|
||||
|
||||
String passPhrase;
|
||||
String passphrase;
|
||||
if (mEditors.getChildCount() > 0) {
|
||||
PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
|
||||
passPhrase = PassphraseCacheService
|
||||
passphrase = PassphraseCacheService
|
||||
.getCachedPassphrase(mActivity, masterKey.getKeyID());
|
||||
isMasterKey = false;
|
||||
} else {
|
||||
passPhrase = "";
|
||||
passphrase = "";
|
||||
isMasterKey = true;
|
||||
}
|
||||
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_KEY_SIZE, mNewKeySize);
|
||||
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
|
||||
// show progress dialog
|
||||
mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating,
|
||||
ProgressDialog.STYLE_SPINNER, true, new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
mActivity.stopService(intent);
|
||||
}
|
||||
});
|
||||
mGeneratingDialog = ProgressDialogFragment.newInstance(
|
||||
getResources().getQuantityString(R.plurals.progress_generating, 1),
|
||||
ProgressDialog.STYLE_SPINNER,
|
||||
true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
mActivity.stopService(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Message is received after generating is done in ApgService
|
||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity,
|
||||
@ -261,7 +270,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
||||
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
|
||||
addGeneratedKeyToView(newKey);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 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.Pattern;
|
||||
|
||||
import android.widget.*;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
|
||||
import android.content.Context;
|
||||
@ -26,11 +27,9 @@ import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
import org.sufficientlysecure.keychain.helper.ContactHelper;
|
||||
|
||||
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
|
||||
private EditorListener mEditorListener = null;
|
||||
@ -38,7 +37,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
|
||||
private BootstrapButton mDeleteButton;
|
||||
private RadioButton mIsMainUserId;
|
||||
private EditText mName;
|
||||
private EditText mEmail;
|
||||
private AutoCompleteTextView mEmail;
|
||||
private EditText mComment;
|
||||
|
||||
// see http://www.regular-expressions.info/email.html
|
||||
@ -102,9 +101,17 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
|
||||
mIsMainUserId.setOnClickListener(this);
|
||||
|
||||
mName = (EditText) findViewById(R.id.name);
|
||||
mEmail = (EditText) findViewById(R.id.email);
|
||||
mEmail = (AutoCompleteTextView) findViewById(R.id.email);
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2011 Thialfihar <thi@thialfihar.org>
|
||||
* Copyright (C) 2011 Senecaso
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -224,7 +226,7 @@ public class HkpKeyServer extends KeyServer {
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
try {
|
||||
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);
|
||||
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
|
||||
|
@ -1,4 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2011 Thialfihar <thi@thialfihar.org>
|
||||
* Copyright (C) 2011 Senecaso
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -16,14 +18,8 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
|
||||
public abstract class KeyServer {
|
||||
@ -52,5 +48,5 @@ public abstract class KeyServer {
|
||||
|
||||
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 |
BIN
OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_search.png
Normal file
After Width: | Height: | Size: 702 B |