keep up with master
3
.gitignore
vendored
@ -28,3 +28,6 @@ pom.xml.*
|
||||
#IntelliJ IDEA
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
#OS Specific
|
||||
[Tt]humbs.db
|
||||
|
16
.travis.yml
Normal file
@ -0,0 +1,16 @@
|
||||
language: java
|
||||
jdk: oraclejdk7
|
||||
before_install:
|
||||
# Install base Android SDK
|
||||
- sudo apt-get update -qq
|
||||
- if [ `uname -m` = x86_64 ]; then sudo apt-get install -qq --force-yes libgd2-xpm ia32-libs; fi
|
||||
- wget http://dl.google.com/android/android-sdk_r22.3-linux.tgz
|
||||
- tar xzf android-sdk_r22.3-linux.tgz
|
||||
- export ANDROID_HOME=$PWD/android-sdk-linux
|
||||
- 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.3,android-19,platform-tools,extra-android-support,extra-android-m2repository --no-ui --force
|
||||
install: echo "Installation done"
|
||||
script: gradle assemble -S -q
|
||||
|
@ -1,3 +1,3 @@
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '1.10'
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.8.3'
|
||||
classpath 'com.android.tools.build:gradle:0.9.0'
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ dependencies {
|
||||
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion "19.0.1"
|
||||
buildToolsVersion "19.0.3"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.sufficientlysecure.keychain.demo"
|
||||
android:versionCode="2"
|
||||
android:versionName="1.1">
|
||||
android:versionCode="3"
|
||||
android:versionName="2">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="9"
|
||||
|
@ -112,11 +112,11 @@ public class IntentActivity extends PreferenceActivity {
|
||||
byte[] pubkey = null;
|
||||
try {
|
||||
pubkey = TEST_PUBKEY.getBytes("UTF-8");
|
||||
intent.putExtra(OpenKeychainIntents.IMPORT_KEY_EXTRA_KEY_BYTES, pubkey);
|
||||
startActivity(intent);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(Constants.TAG, "UnsupportedEncodingException", e);
|
||||
}
|
||||
intent.putExtra(OpenKeychainIntents.IMPORT_KEY_EXTRA_KEY_BYTES, pubkey);
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ import android.widget.Toast;
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.openintents.openpgp.util.OpenPgpConstants;
|
||||
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -73,25 +72,25 @@ public class OpenPgpProviderActivity extends Activity {
|
||||
mSign.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
sign(new Bundle());
|
||||
sign(new Intent());
|
||||
}
|
||||
});
|
||||
mEncrypt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
encrypt(new Bundle());
|
||||
encrypt(new Intent());
|
||||
}
|
||||
});
|
||||
mSignAndEncrypt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
signAndEncrypt(new Bundle());
|
||||
signAndEncrypt(new Intent());
|
||||
}
|
||||
});
|
||||
mDecryptAndVerify.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
decryptAndVerify(new Bundle());
|
||||
decryptAndVerify(new Intent());
|
||||
}
|
||||
});
|
||||
|
||||
@ -169,11 +168,11 @@ public class OpenPgpProviderActivity extends Activity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReturn(Bundle result) {
|
||||
switch (result.getInt(OpenPgpConstants.RESULT_CODE)) {
|
||||
case OpenPgpConstants.RESULT_CODE_SUCCESS: {
|
||||
public void onReturn(Intent result) {
|
||||
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
|
||||
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
||||
try {
|
||||
Log.d(OpenPgpConstants.TAG, "result: " + os.toByteArray().length
|
||||
Log.d(OpenPgpApi.TAG, "result: " + os.toByteArray().length
|
||||
+ " str=" + os.toString("UTF-8"));
|
||||
|
||||
if (returnToCiphertextField) {
|
||||
@ -185,15 +184,15 @@ public class OpenPgpProviderActivity extends Activity {
|
||||
Log.e(Constants.TAG, "UnsupportedEncodingException", e);
|
||||
}
|
||||
|
||||
if (result.getBoolean(OpenPgpConstants.RESULT_SIGNATURE, false)) {
|
||||
if (result.hasExtra(OpenPgpApi.RESULT_SIGNATURE)) {
|
||||
OpenPgpSignatureResult sigResult
|
||||
= result.getParcelable(OpenPgpConstants.RESULT_SIGNATURE);
|
||||
= result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
|
||||
handleSignature(sigResult);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED: {
|
||||
PendingIntent pi = result.getParcelable(OpenPgpConstants.RESULT_INTENT);
|
||||
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
|
||||
PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
|
||||
try {
|
||||
OpenPgpProviderActivity.this.startIntentSenderForResult(pi.getIntentSender(),
|
||||
requestCode, null, 0, 0, 0);
|
||||
@ -202,8 +201,8 @@ public class OpenPgpProviderActivity extends Activity {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenPgpConstants.RESULT_CODE_ERROR: {
|
||||
OpenPgpError error = result.getParcelable(OpenPgpConstants.RESULT_ERRORS);
|
||||
case OpenPgpApi.RESULT_CODE_ERROR: {
|
||||
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
|
||||
handleError(error);
|
||||
break;
|
||||
}
|
||||
@ -211,46 +210,50 @@ public class OpenPgpProviderActivity extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
public void sign(Bundle params) {
|
||||
params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||
public void sign(Intent data) {
|
||||
data.setAction(OpenPgpApi.ACTION_SIGN);
|
||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
|
||||
InputStream is = getInputstream(false);
|
||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.sign(params, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN));
|
||||
api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN));
|
||||
}
|
||||
|
||||
public void encrypt(Bundle params) {
|
||||
params.putStringArray(OpenPgpConstants.PARAMS_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||
params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||
public void encrypt(Intent data) {
|
||||
data.setAction(OpenPgpApi.ACTION_ENCRYPT);
|
||||
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
|
||||
InputStream is = getInputstream(false);
|
||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.encrypt(params, is, os, new MyCallback(true, os, REQUEST_CODE_ENCRYPT));
|
||||
api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_ENCRYPT));
|
||||
}
|
||||
|
||||
public void signAndEncrypt(Bundle params) {
|
||||
params.putStringArray(OpenPgpConstants.PARAMS_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||
params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||
public void signAndEncrypt(Intent data) {
|
||||
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);
|
||||
|
||||
InputStream is = getInputstream(false);
|
||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.signAndEncrypt(params, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN_AND_ENCRYPT));
|
||||
api.executeApiAsync(data, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN_AND_ENCRYPT));
|
||||
}
|
||||
|
||||
public void decryptAndVerify(Bundle params) {
|
||||
params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||
public void decryptAndVerify(Intent data) {
|
||||
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
|
||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
|
||||
InputStream is = getInputstream(true);
|
||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||
api.decryptAndVerify(params, is, os, new MyCallback(false, os, REQUEST_CODE_DECRYPT_AND_VERIFY));
|
||||
api.executeApiAsync(data, is, os, new MyCallback(false, os, REQUEST_CODE_DECRYPT_AND_VERIFY));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -261,29 +264,28 @@ public class OpenPgpProviderActivity extends Activity {
|
||||
// try again after user interaction
|
||||
if (resultCode == RESULT_OK) {
|
||||
/*
|
||||
* The params originally given to the pgp method are are again
|
||||
* The data originally given to the pgp method are are again
|
||||
* returned here to be used when calling again after user interaction.
|
||||
*
|
||||
* They also contain results from the user interaction which happened,
|
||||
* for example selected key ids.
|
||||
*/
|
||||
Bundle params = data.getBundleExtra(OpenPgpConstants.PI_RESULT_PARAMS);
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_SIGN: {
|
||||
sign(params);
|
||||
sign(data);
|
||||
break;
|
||||
}
|
||||
case REQUEST_CODE_ENCRYPT: {
|
||||
encrypt(params);
|
||||
encrypt(data);
|
||||
break;
|
||||
}
|
||||
case REQUEST_CODE_SIGN_AND_ENCRYPT: {
|
||||
signAndEncrypt(params);
|
||||
signAndEncrypt(data);
|
||||
break;
|
||||
}
|
||||
case REQUEST_CODE_DECRYPT_AND_VERIFY: {
|
||||
decryptAndVerify(params);
|
||||
decryptAndVerify(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -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 *;
|
||||
#}
|
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.3 KiB |
@ -2,5 +2,6 @@
|
||||
<resources>
|
||||
|
||||
<string name="openpgp_list_preference_none">None</string>
|
||||
<string name="openpgp_install_openkeychain_via">Install OpenKeychain via %s</string>
|
||||
|
||||
</resources>
|
@ -18,68 +18,7 @@ package org.openintents.openpgp;
|
||||
|
||||
interface IOpenPgpService {
|
||||
|
||||
/**
|
||||
* General extras
|
||||
* --------------
|
||||
*
|
||||
* Bundle params:
|
||||
* int api_version (required)
|
||||
* boolean ascii_armor (request ascii armor for ouput)
|
||||
*
|
||||
* returned Bundle:
|
||||
* int result_code (0, 1, or 2 (see OpenPgpConstants))
|
||||
* OpenPgpError error (if result_code == 0)
|
||||
* Intent intent (if result_code == 2)
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sign only
|
||||
*
|
||||
* optional params:
|
||||
* String passphrase (for key passphrase)
|
||||
*/
|
||||
Bundle sign(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||
|
||||
/**
|
||||
* Encrypt
|
||||
*
|
||||
* Bundle params:
|
||||
* long[] key_ids
|
||||
* or
|
||||
* String[] user_ids (= emails of recipients) (if more than one key has this user_id, a PendingIntent is returned)
|
||||
*
|
||||
* optional params:
|
||||
* String passphrase (for key passphrase)
|
||||
*/
|
||||
Bundle encrypt(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||
|
||||
/**
|
||||
* Sign and encrypt
|
||||
*
|
||||
* Bundle params:
|
||||
* same as in encrypt()
|
||||
*/
|
||||
Bundle signAndEncrypt(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||
|
||||
/**
|
||||
* Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
|
||||
* and also signed-only input.
|
||||
*
|
||||
* returned Bundle:
|
||||
* OpenPgpSignatureResult signature_result
|
||||
*/
|
||||
Bundle decryptAndVerify(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||
|
||||
/**
|
||||
* Retrieves key ids based on given user ids (=emails)
|
||||
*
|
||||
* Bundle params:
|
||||
* String[] user_ids
|
||||
*
|
||||
* returned Bundle:
|
||||
* long[] key_ids
|
||||
*/
|
||||
Bundle getKeyIds(in Bundle params);
|
||||
// see OpenPgpApi for documentation
|
||||
Intent execute(in Intent data, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||
|
||||
}
|
@ -25,10 +25,8 @@ public class OpenPgpSignatureResult implements Parcelable {
|
||||
// successfully verified signature, with certified public key
|
||||
public static final int SIGNATURE_SUCCESS_CERTIFIED = 1;
|
||||
// no public key was found for this signature verification
|
||||
// you can retrieve the key with
|
||||
// getKeys(new String[] {String.valueOf(signatureResult.getKeyId)}, true, callback)
|
||||
public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2;
|
||||
// successfully verified signature, but with certified public key
|
||||
// successfully verified signature, but with uncertified public key
|
||||
public static final int SIGNATURE_SUCCESS_UNCERTIFIED = 3;
|
||||
|
||||
int status;
|
||||
@ -40,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,137 +16,213 @@
|
||||
|
||||
package org.openintents.openpgp.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import org.openintents.openpgp.IOpenPgpService;
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class OpenPgpApi {
|
||||
|
||||
public static final String TAG = "OpenPgp API";
|
||||
|
||||
public static final int API_VERSION = 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";
|
||||
|
||||
/**
|
||||
* 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";
|
||||
|
||||
/**
|
||||
* 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";
|
||||
|
||||
/**
|
||||
* 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_AND_ENCRYPT, DECRYPT_VERIFY
|
||||
// request ASCII Armor for output
|
||||
// OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
|
||||
public static final String EXTRA_REQUEST_ASCII_ARMOR = "ascii_armor";
|
||||
|
||||
// ENCRYPT, SIGN_AND_ENCRYPT
|
||||
public static final String EXTRA_USER_IDS = "user_ids";
|
||||
public static final String EXTRA_KEY_IDS = "key_ids";
|
||||
// optional extras:
|
||||
public static final String EXTRA_PASSPHRASE = "passphrase";
|
||||
|
||||
// GET_KEY
|
||||
public static final String EXTRA_KEY_ID = "key_id";
|
||||
|
||||
/* Service Intent returns */
|
||||
public static final String RESULT_CODE = "result_code";
|
||||
|
||||
// get actual error object from RESULT_ERROR
|
||||
public static final int RESULT_CODE_ERROR = 0;
|
||||
// success!
|
||||
public static final int RESULT_CODE_SUCCESS = 1;
|
||||
// get PendingIntent from RESULT_INTENT, start PendingIntent with startIntentSenderForResult,
|
||||
// and execute service method again in onActivityResult
|
||||
public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2;
|
||||
|
||||
public static final String RESULT_ERROR = "error";
|
||||
public static final String RESULT_INTENT = "intent";
|
||||
|
||||
// DECRYPT_VERIFY
|
||||
public static final String RESULT_SIGNATURE = "signature";
|
||||
|
||||
IOpenPgpService mService;
|
||||
Context mContext;
|
||||
|
||||
private static final int OPERATION_SIGN = 0;
|
||||
private static final int OPERATION_ENCRYPT = 1;
|
||||
private static final int OPERATION_SIGN_ENCRYPT = 2;
|
||||
private static final int OPERATION_DECRYPT_VERIFY = 3;
|
||||
private static final int OPERATION_GET_KEY_IDS = 4;
|
||||
|
||||
public OpenPgpApi(Context context, IOpenPgpService service) {
|
||||
this.mContext = context;
|
||||
this.mService = service;
|
||||
}
|
||||
|
||||
public Bundle sign(InputStream is, final OutputStream os) {
|
||||
return executeApi(OPERATION_SIGN, new Bundle(), is, os);
|
||||
}
|
||||
|
||||
public Bundle sign(Bundle params, InputStream is, final OutputStream os) {
|
||||
return executeApi(OPERATION_SIGN, params, is, os);
|
||||
}
|
||||
|
||||
public void sign(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
|
||||
executeApiAsync(OPERATION_SIGN, params, is, os, callback);
|
||||
}
|
||||
|
||||
public Bundle encrypt(InputStream is, final OutputStream os) {
|
||||
return executeApi(OPERATION_ENCRYPT, new Bundle(), is, os);
|
||||
}
|
||||
|
||||
public Bundle encrypt(Bundle params, InputStream is, final OutputStream os) {
|
||||
return executeApi(OPERATION_ENCRYPT, params, is, os);
|
||||
}
|
||||
|
||||
public void encrypt(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
|
||||
executeApiAsync(OPERATION_ENCRYPT, params, is, os, callback);
|
||||
}
|
||||
|
||||
public Bundle signAndEncrypt(InputStream is, final OutputStream os) {
|
||||
return executeApi(OPERATION_SIGN_ENCRYPT, new Bundle(), is, os);
|
||||
}
|
||||
|
||||
public Bundle signAndEncrypt(Bundle params, InputStream is, final OutputStream os) {
|
||||
return executeApi(OPERATION_SIGN_ENCRYPT, params, is, os);
|
||||
}
|
||||
|
||||
public void signAndEncrypt(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
|
||||
executeApiAsync(OPERATION_SIGN_ENCRYPT, params, is, os, callback);
|
||||
}
|
||||
|
||||
public Bundle decryptAndVerify(InputStream is, final OutputStream os) {
|
||||
return executeApi(OPERATION_DECRYPT_VERIFY, new Bundle(), is, os);
|
||||
}
|
||||
|
||||
public Bundle decryptAndVerify(Bundle params, InputStream is, final OutputStream os) {
|
||||
return executeApi(OPERATION_DECRYPT_VERIFY, params, is, os);
|
||||
}
|
||||
|
||||
public void decryptAndVerify(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
|
||||
executeApiAsync(OPERATION_DECRYPT_VERIFY, params, is, os, callback);
|
||||
}
|
||||
|
||||
public Bundle getKeyIds(Bundle params) {
|
||||
return executeApi(OPERATION_GET_KEY_IDS, params, null, null);
|
||||
}
|
||||
|
||||
public interface IOpenPgpCallback {
|
||||
void onReturn(final Bundle result);
|
||||
void onReturn(final Intent result);
|
||||
}
|
||||
|
||||
private class OpenPgpAsyncTask extends AsyncTask<Void, Integer, Bundle> {
|
||||
int operationId;
|
||||
Bundle params;
|
||||
private class OpenPgpAsyncTask extends AsyncTask<Void, Integer, Intent> {
|
||||
Intent data;
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
IOpenPgpCallback callback;
|
||||
|
||||
private OpenPgpAsyncTask(int operationId, Bundle params, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
||||
this.operationId = operationId;
|
||||
this.params = params;
|
||||
private OpenPgpAsyncTask(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
||||
this.data = data;
|
||||
this.is = is;
|
||||
this.os = os;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doInBackground(Void... unused) {
|
||||
return executeApi(operationId, params, is, os);
|
||||
protected Intent doInBackground(Void... unused) {
|
||||
return executeApi(data, is, os);
|
||||
}
|
||||
|
||||
protected void onPostExecute(Bundle result) {
|
||||
protected void onPostExecute(Intent result) {
|
||||
callback.onReturn(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void executeApiAsync(int operationId, Bundle params, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
||||
new OpenPgpAsyncTask(operationId, params, is, os, callback).execute((Void[]) null);
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public void executeApiAsync(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
||||
OpenPgpAsyncTask task = new OpenPgpAsyncTask(data, is, os, callback);
|
||||
|
||||
// don't serialize async tasks!
|
||||
// http://commonsware.com/blog/2012/04/20/asynctask-threading-regression-confirmed.html
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
|
||||
} else {
|
||||
task.execute((Void[]) null);
|
||||
}
|
||||
}
|
||||
|
||||
private Bundle executeApi(int operationId, Bundle params, InputStream is, OutputStream os) {
|
||||
public Intent executeApi(Intent data, InputStream is, OutputStream os) {
|
||||
try {
|
||||
params.putInt(OpenPgpConstants.PARAMS_API_VERSION, OpenPgpConstants.API_VERSION);
|
||||
data.putExtra(EXTRA_API_VERSION, OpenPgpApi.API_VERSION);
|
||||
|
||||
Bundle result = null;
|
||||
Intent result = null;
|
||||
|
||||
if (operationId == OPERATION_GET_KEY_IDS) {
|
||||
result = mService.getKeyIds(params);
|
||||
if (ACTION_GET_KEY_IDS.equals(data.getAction())) {
|
||||
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(OpenPgpConstants.TAG, "Copy to service finished");
|
||||
//Log.d(OpenPgpApi.TAG, "Copy to service finished");
|
||||
}
|
||||
});
|
||||
ParcelFileDescriptor output = ParcelFileDescriptorUtil.pipeTo(os,
|
||||
@ -154,45 +230,30 @@ public class OpenPgpApi {
|
||||
|
||||
@Override
|
||||
public void onThreadFinished(Thread thread) {
|
||||
Log.d(OpenPgpConstants.TAG, "Service finished writing!");
|
||||
//Log.d(OpenPgpApi.TAG, "Service finished writing!");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// blocks until result is ready
|
||||
switch (operationId) {
|
||||
case OPERATION_SIGN:
|
||||
result = mService.sign(params, input, output);
|
||||
break;
|
||||
case OPERATION_ENCRYPT:
|
||||
result = mService.encrypt(params, input, output);
|
||||
break;
|
||||
case OPERATION_SIGN_ENCRYPT:
|
||||
result = mService.signAndEncrypt(params, input, output);
|
||||
break;
|
||||
case OPERATION_DECRYPT_VERIFY:
|
||||
result = mService.decryptAndVerify(params, input, output);
|
||||
break;
|
||||
}
|
||||
result = mService.execute(data, input, output);
|
||||
// close() is required to halt the TransferThread
|
||||
output.close();
|
||||
|
||||
// set class loader to current context to allow unparcelling
|
||||
// of OpenPgpError and OpenPgpSignatureResult
|
||||
// http://stackoverflow.com/a/3806769
|
||||
result.setClassLoader(mContext.getClassLoader());
|
||||
result.setExtrasClassLoader(mContext.getClassLoader());
|
||||
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(OpenPgpConstants.TAG, "Exception", e);
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||
Log.e(OpenPgpApi.TAG, "Exception", e);
|
||||
Intent result = new Intent();
|
||||
result.putExtra(RESULT_CODE, RESULT_CODE_ERROR);
|
||||
result.putExtra(RESULT_ERROR,
|
||||
new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openintents.openpgp.util;
|
||||
|
||||
public class OpenPgpConstants {
|
||||
|
||||
public static final String TAG = "OpenPgp API";
|
||||
|
||||
public static final int API_VERSION = 1;
|
||||
public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
|
||||
|
||||
|
||||
/* Bundle params */
|
||||
public static final String PARAMS_API_VERSION = "api_version";
|
||||
// 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 PARAMS_REQUEST_ASCII_ARMOR = "ascii_armor";
|
||||
// (for encrypt method)
|
||||
public static final String PARAMS_USER_IDS = "user_ids";
|
||||
public static final String PARAMS_KEY_IDS = "key_ids";
|
||||
// optional parameter:
|
||||
public static final String PARAMS_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 actual error object from RESULT_ERRORS
|
||||
public static final int RESULT_CODE_ERROR = 0;
|
||||
// success!
|
||||
public static final int RESULT_CODE_SUCCESS = 1;
|
||||
// executeServiceMethod intent and do it again with params from intent
|
||||
public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2;
|
||||
|
||||
/* PendingIntent returns */
|
||||
public static final String PI_RESULT_PARAMS = "params";
|
||||
|
||||
}
|
@ -23,6 +23,7 @@ import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.preference.DialogPreference;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
@ -30,18 +31,24 @@ import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.TextView;
|
||||
import org.sufficientlysecure.keychain.api.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.sufficientlysecure.keychain.api.R;
|
||||
|
||||
/**
|
||||
* Does not extend ListPreference, but is very similar to it!
|
||||
* http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/preference/ListPreference.java/?v=source
|
||||
*/
|
||||
public class OpenPgpListPreference extends DialogPreference {
|
||||
private ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>();
|
||||
private static final String OPENKEYCHAIN_PACKAGE = "org.sufficientlysecure.keychain";
|
||||
private static final String MARKET_INTENT_URI_BASE = "market://details?id=%s";
|
||||
private static final Intent MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse(
|
||||
String.format(MARKET_INTENT_URI_BASE, OPENKEYCHAIN_PACKAGE)));
|
||||
|
||||
private ArrayList<OpenPgpProviderEntry> mLegacyList = new ArrayList<OpenPgpProviderEntry>();
|
||||
private ArrayList<OpenPgpProviderEntry> mList = new ArrayList<OpenPgpProviderEntry>();
|
||||
|
||||
private String mSelectedPackage;
|
||||
|
||||
public OpenPgpListPreference(Context context, AttributeSet attrs) {
|
||||
@ -59,16 +66,25 @@ public class OpenPgpListPreference extends DialogPreference {
|
||||
* @param simpleName
|
||||
* @param icon
|
||||
*/
|
||||
public void addProvider(int position, String packageName, String simpleName, Drawable icon) {
|
||||
mProviderList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon));
|
||||
public void addLegacyProvider(int position, String packageName, String simpleName, Drawable icon) {
|
||||
mLegacyList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialogBuilder(Builder builder) {
|
||||
|
||||
// get providers
|
||||
mProviderList.clear();
|
||||
Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
|
||||
mList.clear();
|
||||
|
||||
// add "none"-entry
|
||||
mList.add(0, new OpenPgpProviderEntry("",
|
||||
getContext().getString(R.string.openpgp_list_preference_none),
|
||||
getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize)));
|
||||
|
||||
// add all additional (legacy) providers
|
||||
mList.addAll(mLegacyList);
|
||||
|
||||
// search for OpenPGP providers...
|
||||
ArrayList<OpenPgpProviderEntry> providerList = new ArrayList<OpenPgpProviderEntry>();
|
||||
Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT);
|
||||
List<ResolveInfo> resInfo = getContext().getPackageManager().queryIntentServices(intent, 0);
|
||||
if (!resInfo.isEmpty()) {
|
||||
for (ResolveInfo resolveInfo : resInfo) {
|
||||
@ -80,25 +96,40 @@ public class OpenPgpListPreference extends DialogPreference {
|
||||
.getPackageManager()));
|
||||
Drawable icon = resolveInfo.serviceInfo.loadIcon(getContext().getPackageManager());
|
||||
|
||||
mProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
|
||||
providerList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
|
||||
}
|
||||
}
|
||||
|
||||
// add "none"-entry
|
||||
mProviderList.add(0, new OpenPgpProviderEntry("",
|
||||
getContext().getString(R.string.openpgp_list_preference_none),
|
||||
getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize)));
|
||||
if (providerList.isEmpty()) {
|
||||
// add install links if provider list is empty
|
||||
resInfo = getContext().getPackageManager().queryIntentActivities
|
||||
(MARKET_INTENT, 0);
|
||||
for (ResolveInfo resolveInfo : resInfo) {
|
||||
Intent marketIntent = new Intent(MARKET_INTENT);
|
||||
marketIntent.setPackage(resolveInfo.activityInfo.packageName);
|
||||
Drawable icon = resolveInfo.activityInfo.loadIcon(getContext().getPackageManager());
|
||||
String marketName = String.valueOf(resolveInfo.activityInfo.applicationInfo
|
||||
.loadLabel(getContext().getPackageManager()));
|
||||
String simpleName = String.format(getContext().getString(R.string
|
||||
.openpgp_install_openkeychain_via), marketName);
|
||||
mList.add(new OpenPgpProviderEntry(OPENKEYCHAIN_PACKAGE, simpleName,
|
||||
icon, marketIntent));
|
||||
}
|
||||
} else {
|
||||
// add provider
|
||||
mList.addAll(providerList);
|
||||
}
|
||||
|
||||
// Init ArrayAdapter with OpenPGP Providers
|
||||
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
|
||||
android.R.layout.select_dialog_singlechoice, android.R.id.text1, mProviderList) {
|
||||
android.R.layout.select_dialog_singlechoice, android.R.id.text1, mList) {
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
// User super class to create the View
|
||||
View v = super.getView(position, convertView, parent);
|
||||
TextView tv = (TextView) v.findViewById(android.R.id.text1);
|
||||
|
||||
// Put the image on the TextView
|
||||
tv.setCompoundDrawablesWithIntrinsicBounds(mProviderList.get(position).icon, null,
|
||||
tv.setCompoundDrawablesWithIntrinsicBounds(mList.get(position).icon, null,
|
||||
null, null);
|
||||
|
||||
// Add margin between image and text (support various screen densities)
|
||||
@ -114,7 +145,22 @@ public class OpenPgpListPreference extends DialogPreference {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mSelectedPackage = mProviderList.get(which).packageName;
|
||||
OpenPgpProviderEntry entry = mList.get(which);
|
||||
|
||||
if (entry.intent != null) {
|
||||
/*
|
||||
* Intents are called as activity
|
||||
*
|
||||
* Current approach is to assume the user installed the app.
|
||||
* If he does not, the selected package is not valid.
|
||||
*
|
||||
* However applications should always consider this could happen,
|
||||
* as the user might remove the currently used OpenPGP app.
|
||||
*/
|
||||
getContext().startActivity(entry.intent);
|
||||
}
|
||||
|
||||
mSelectedPackage = entry.packageName;
|
||||
|
||||
/*
|
||||
* Clicking on an item simulates the positive button click, and dismisses
|
||||
@ -144,9 +190,9 @@ public class OpenPgpListPreference extends DialogPreference {
|
||||
}
|
||||
|
||||
private int getIndexOfProviderList(String packageName) {
|
||||
for (OpenPgpProviderEntry app : mProviderList) {
|
||||
for (OpenPgpProviderEntry app : mList) {
|
||||
if (app.packageName.equals(packageName)) {
|
||||
return mProviderList.indexOf(app);
|
||||
return mList.indexOf(app);
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,7 +223,7 @@ public class OpenPgpListPreference extends DialogPreference {
|
||||
}
|
||||
|
||||
public String getEntryByValue(String packageName) {
|
||||
for (OpenPgpProviderEntry app : mProviderList) {
|
||||
for (OpenPgpProviderEntry app : mList) {
|
||||
if (app.packageName.equals(packageName)) {
|
||||
return app.simpleName;
|
||||
}
|
||||
@ -190,6 +236,7 @@ public class OpenPgpListPreference extends DialogPreference {
|
||||
private String packageName;
|
||||
private String simpleName;
|
||||
private Drawable icon;
|
||||
private Intent intent;
|
||||
|
||||
public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon) {
|
||||
this.packageName = packageName;
|
||||
@ -197,6 +244,11 @@ public class OpenPgpListPreference extends DialogPreference {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon, Intent intent) {
|
||||
this(packageName, simpleName, icon);
|
||||
this.intent = intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return simpleName;
|
||||
|
@ -52,7 +52,7 @@ public class OpenPgpUtils {
|
||||
}
|
||||
|
||||
public static boolean isAvailable(Context context) {
|
||||
Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
|
||||
Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT);
|
||||
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
|
||||
if (!resInfo.isEmpty()) {
|
||||
return true;
|
||||
|
@ -81,21 +81,21 @@ public class ParcelFileDescriptorUtil {
|
||||
}
|
||||
mOut.flush(); // just to be safe
|
||||
} catch (IOException e) {
|
||||
//Log.e(OpenPgpConstants.TAG, "TransferThread" + getId() + ": writing failed", e);
|
||||
//Log.e(OpenPgpApi.TAG, "TransferThread" + getId() + ": writing failed", e);
|
||||
} finally {
|
||||
try {
|
||||
mIn.close();
|
||||
} catch (IOException e) {
|
||||
//Log.e(OpenPgpConstants.TAG, "TransferThread" + getId(), e);
|
||||
//Log.e(OpenPgpApi.TAG, "TransferThread" + getId(), e);
|
||||
}
|
||||
try {
|
||||
mOut.close();
|
||||
} catch (IOException e) {
|
||||
//Log.e(OpenPgpConstants.TAG, "TransferThread" + getId(), e);
|
||||
//Log.e(OpenPgpApi.TAG, "TransferThread" + getId(), e);
|
||||
}
|
||||
}
|
||||
if (mListener != null) {
|
||||
//Log.d(OpenPgpConstants.TAG, "TransferThread " + getId() + " finished!");
|
||||
//Log.d(OpenPgpApi.TAG, "TransferThread " + getId() + " finished!");
|
||||
mListener.onThreadFinished(this);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ apply plugin: 'android'
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:19.0.1'
|
||||
compile 'com.android.support:appcompat-v7:19.0.1'
|
||||
compile project(':libraries:keychain-api-library')
|
||||
compile project(':OpenPGP-Keychain-API:libraries:keychain-api-library')
|
||||
compile project(':libraries:HtmlTextView')
|
||||
compile project(':libraries:StickyListHeaders:library')
|
||||
compile project(':libraries:AndroidBootstrap')
|
||||
@ -18,7 +18,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="23100"
|
||||
android:versionName="2.3.1">
|
||||
android:versionCode="23103"
|
||||
android:versionName="2.3.1 beta3">
|
||||
|
||||
<!--
|
||||
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!
|
||||
@ -68,30 +69,12 @@
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- <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.KeyListSecretActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_manage_secret_keys"
|
||||
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.EditKeyActivity"
|
||||
@ -121,30 +104,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"
|
||||
@ -276,7 +241,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"
|
||||
@ -454,10 +428,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 -->
|
||||
|
@ -51,7 +51,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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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];
|
||||
}
|
||||
};
|
||||
}
|
@ -90,8 +90,8 @@ public class PgpImportExport {
|
||||
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) {
|
||||
|
@ -105,7 +105,7 @@ public class PgpKeyOperation {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@ -113,8 +113,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;
|
||||
@ -168,7 +168,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());
|
||||
|
||||
return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
|
||||
sha1Calc, isMasterKey, keyEncryptor);
|
||||
|
@ -626,7 +626,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,6 +44,7 @@ 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;
|
||||
@ -176,13 +177,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";
|
||||
@ -198,8 +193,16 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
|
||||
Messenger mMessenger;
|
||||
|
||||
private boolean mIsCanceled;
|
||||
|
||||
public KeychainIntentService() {
|
||||
super("ApgService");
|
||||
super("KeychainIntentService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
this.mIsCanceled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,15 +479,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) {
|
||||
@ -733,7 +738,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 {
|
||||
|
||||
@ -805,6 +810,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
}
|
||||
|
||||
private void sendErrorToHandler(Exception e) {
|
||||
// Service was canceled. Do not send error to handler.
|
||||
if (this.mIsCanceled)
|
||||
return;
|
||||
|
||||
Log.e(Constants.TAG, "ApgService Exception: ", e);
|
||||
e.printStackTrace();
|
||||
|
||||
@ -814,6 +823,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
}
|
||||
|
||||
private void sendMessageToHandler(Integer arg1, Integer arg2, Bundle data) {
|
||||
// Service was canceled. Do not send message to handler.
|
||||
if (this.mIsCanceled)
|
||||
return;
|
||||
|
||||
Message msg = Message.obtain();
|
||||
msg.arg1 = arg1;
|
||||
if (arg2 != null) {
|
||||
@ -841,10 +854,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,6 +21,8 @@ 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;
|
||||
import android.os.Message;
|
||||
@ -55,9 +57,15 @@ public class KeychainIntentServiceHandler extends Handler {
|
||||
}
|
||||
|
||||
public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) {
|
||||
this(activity, progressDialogMessageId, progressDialogStyle, false, null);
|
||||
}
|
||||
|
||||
public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId,
|
||||
int progressDialogStyle, boolean cancelable,
|
||||
OnCancelListener onCancelListener) {
|
||||
this.mActivity = activity;
|
||||
this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId,
|
||||
progressDialogStyle);
|
||||
progressDialogStyle, cancelable, onCancelListener);
|
||||
}
|
||||
|
||||
public void showProgressDialog(FragmentActivity activity) {
|
||||
|
@ -21,21 +21,22 @@ 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;
|
||||
|
||||
import org.openintents.openpgp.IOpenPgpService;
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.openintents.openpgp.util.OpenPgpConstants;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
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;
|
||||
@ -48,7 +49,7 @@ public class OpenPgpService extends RemoteService {
|
||||
|
||||
private static final int PRIVATE_REQUEST_CODE_PASSPHRASE = 551;
|
||||
private static final int PRIVATE_REQUEST_CODE_USER_IDS = 552;
|
||||
|
||||
private static final int PRIVATE_REQUEST_CODE_GET_KEYS = 553;
|
||||
|
||||
/**
|
||||
* Search database for key ids based on emails.
|
||||
@ -56,7 +57,7 @@ public class OpenPgpService extends RemoteService {
|
||||
* @param encryptionUserIds
|
||||
* @return
|
||||
*/
|
||||
private Bundle getKeyIdsFromEmails(Bundle params, String[] encryptionUserIds) {
|
||||
private Intent getKeyIdsFromEmails(Intent data, String[] encryptionUserIds) {
|
||||
// find key ids to given emails in database
|
||||
ArrayList<Long> keyIds = new ArrayList<Long>();
|
||||
|
||||
@ -91,21 +92,20 @@ public class OpenPgpService extends RemoteService {
|
||||
|
||||
// allow the user to verify pub key selection
|
||||
if (missingUserIdsCheck || dublicateUserIdsCheck) {
|
||||
// build PendingIntent for passphrase input
|
||||
// build PendingIntent
|
||||
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||
intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds);
|
||||
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||
|
||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_USER_IDS, intent, 0);
|
||||
|
||||
// return PendingIntent to be executed by client
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
|
||||
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -113,44 +113,43 @@ public class OpenPgpService extends RemoteService {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
|
||||
result.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS, keyIdsArray);
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keyIdsArray);
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Bundle getPassphraseBundleIntent(Bundle params, long keyId) {
|
||||
private Intent getPassphraseBundleIntent(Intent data, long keyId) {
|
||||
// build PendingIntent for passphrase input
|
||||
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||
intent.setAction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
|
||||
// pass params through to activity that it can be returned again later to repeat pgp operation
|
||||
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_PASSPHRASE, intent, 0);
|
||||
|
||||
// return PendingIntent to be executed by client
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
|
||||
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Bundle signImpl(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output,
|
||||
AppSettings appSettings) {
|
||||
private Intent signImpl(Intent data, ParcelFileDescriptor input,
|
||||
ParcelFileDescriptor output, AppSettings appSettings) {
|
||||
try {
|
||||
boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
|
||||
// get passphrase from cache, if key has "no" passphrase, this returns an empty String
|
||||
String passphrase;
|
||||
if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
|
||||
passphrase = params.getString(OpenPgpConstants.PARAMS_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
|
||||
Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
|
||||
Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
|
||||
return passphraseBundle;
|
||||
}
|
||||
|
||||
@ -174,44 +173,43 @@ public class OpenPgpService extends RemoteService {
|
||||
os.close();
|
||||
}
|
||||
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private Bundle encryptAndSignImpl(Bundle params, ParcelFileDescriptor input,
|
||||
ParcelFileDescriptor output, AppSettings appSettings,
|
||||
boolean sign) {
|
||||
private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input,
|
||||
ParcelFileDescriptor output, AppSettings appSettings, boolean sign) {
|
||||
try {
|
||||
boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
|
||||
long[] keyIds;
|
||||
if (params.containsKey(OpenPgpConstants.PARAMS_KEY_IDS)) {
|
||||
keyIds = params.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS);
|
||||
} else if (params.containsKey(OpenPgpConstants.PARAMS_USER_IDS)) {
|
||||
if (data.hasExtra(OpenPgpApi.EXTRA_KEY_IDS)) {
|
||||
keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
|
||||
} else if (data.hasExtra(OpenPgpApi.EXTRA_USER_IDS)) {
|
||||
// get key ids based on given user ids
|
||||
String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS);
|
||||
String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS);
|
||||
// give params through to activity...
|
||||
Bundle result = getKeyIdsFromEmails(params, userIds);
|
||||
Intent result = getKeyIdsFromEmails(data, userIds);
|
||||
|
||||
if (result.getInt(OpenPgpConstants.RESULT_CODE, 0) == OpenPgpConstants.RESULT_CODE_SUCCESS) {
|
||||
keyIds = result.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS);
|
||||
if (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0) == OpenPgpApi.RESULT_CODE_SUCCESS) {
|
||||
keyIds = result.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
|
||||
} else {
|
||||
// if not success -> result contains a PendingIntent for user interaction
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||
Intent result = new Intent();
|
||||
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;
|
||||
}
|
||||
|
||||
@ -235,15 +233,15 @@ public class OpenPgpService extends RemoteService {
|
||||
|
||||
if (sign) {
|
||||
String passphrase;
|
||||
if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
|
||||
passphrase = params.getString(OpenPgpConstants.PARAMS_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
|
||||
Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
|
||||
Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
|
||||
return passphraseBundle;
|
||||
}
|
||||
|
||||
@ -263,193 +261,123 @@ public class OpenPgpService extends RemoteService {
|
||||
os.close();
|
||||
}
|
||||
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private Bundle decryptAndVerifyImpl(Bundle params, ParcelFileDescriptor input,
|
||||
private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input,
|
||||
ParcelFileDescriptor output, AppSettings appSettings) {
|
||||
try {
|
||||
// Get Input- and OutputStream from ParcelFileDescriptor
|
||||
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
|
||||
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
|
||||
OpenPgpSignatureResult sigResult = null;
|
||||
|
||||
Intent result = new Intent();
|
||||
try {
|
||||
|
||||
// PGPUtil.getDecoderStream(is)
|
||||
// TODOs API 2.0:
|
||||
// implement verify-only!
|
||||
// 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: this is not really needed
|
||||
// checked if it is text with BEGIN and END tags
|
||||
// String message = new String(inputBytes);
|
||||
// Log.d(Constants.TAG, "in: " + message);
|
||||
// boolean signedOnly = false;
|
||||
// Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(message);
|
||||
// if (matcher.matches()) {
|
||||
// Log.d(Constants.TAG, "PGP_MESSAGE matched");
|
||||
// message = matcher.group(1);
|
||||
// // replace non breakable spaces
|
||||
// message = message.replaceAll("\\xa0", " ");
|
||||
//
|
||||
// // overwrite inputBytes
|
||||
// inputBytes = message.getBytes();
|
||||
// } else {
|
||||
// matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(message);
|
||||
// if (matcher.matches()) {
|
||||
// signedOnly = true;
|
||||
// Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
|
||||
// message = matcher.group(1);
|
||||
// // replace non breakable spaces
|
||||
// message = message.replaceAll("\\xa0", " ");
|
||||
//
|
||||
// // overwrite inputBytes
|
||||
// inputBytes = message.getBytes();
|
||||
// } else {
|
||||
// Log.d(Constants.TAG, "Nothing matched! Binary?");
|
||||
// }
|
||||
// }
|
||||
// END TODO
|
||||
|
||||
// Log.d(Constants.TAG, "in: " + new String(inputBytes));
|
||||
|
||||
// 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 (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
|
||||
passphrase = params.getString(OpenPgpConstants.PARAMS_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
|
||||
Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
|
||||
return passphraseBundle;
|
||||
}
|
||||
// }
|
||||
|
||||
// build InputData and write into OutputStream
|
||||
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);
|
||||
|
||||
// if (signedOnly) {
|
||||
// outputBundle = builder.build().verifyText();
|
||||
// } else {
|
||||
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);
|
||||
|
||||
// Do we want to do this: instead of trying to get the passphrase before
|
||||
// pause stream when passphrase is missing and then resume???
|
||||
// TODO: currently does not support binary signed-only content
|
||||
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
|
||||
|
||||
// TODO: this also decrypts with other secret keys without passphrase!!!
|
||||
outputBundle = builder.build().execute();
|
||||
// }
|
||||
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!");
|
||||
}
|
||||
|
||||
// outputStream.close();
|
||||
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);
|
||||
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||
|
||||
// byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
|
||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
|
||||
PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
|
||||
|
||||
// get signature informations from bundle
|
||||
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);
|
||||
|
||||
int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
|
||||
if (signatureSuccess) {
|
||||
signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED;
|
||||
} else if (signatureUnknown) {
|
||||
signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
|
||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||
}
|
||||
|
||||
sigResult = new OpenPgpSignatureResult(signatureStatus, signatureUserId,
|
||||
signatureOnly, signatureKeyId);
|
||||
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
|
||||
}
|
||||
|
||||
} finally {
|
||||
is.close();
|
||||
os.close();
|
||||
}
|
||||
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_SIGNATURE, sigResult);
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private Bundle getKeyIdsImpl(Bundle params) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private Intent getKeyIdsImpl(Intent data) {
|
||||
// get key ids based on given user ids
|
||||
String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS);
|
||||
Bundle result = getKeyIdsFromEmails(params, userIds);
|
||||
String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS);
|
||||
Intent result = getKeyIdsFromEmails(data, userIds);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -459,30 +387,30 @@ public class OpenPgpService extends RemoteService {
|
||||
* - has supported API version
|
||||
* - is allowed to call the service (access has been granted)
|
||||
*
|
||||
* @param params
|
||||
* @param data
|
||||
* @return null if everything is okay, or a Bundle with an error/PendingIntent
|
||||
*/
|
||||
private Bundle checkRequirements(Bundle params) {
|
||||
private Intent checkRequirements(Intent data) {
|
||||
// params Bundle is required!
|
||||
if (params == null) {
|
||||
Bundle result = new Bundle();
|
||||
if (data == null) {
|
||||
Intent result = new Intent();
|
||||
OpenPgpError error = new OpenPgpError(OpenPgpError.GENERIC_ERROR, "params Bundle required!");
|
||||
result.putParcelable(OpenPgpConstants.RESULT_ERRORS, error);
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||
return result;
|
||||
}
|
||||
|
||||
// version code is required and needs to correspond to version code of service!
|
||||
if (params.getInt(OpenPgpConstants.PARAMS_API_VERSION) != OpenPgpConstants.API_VERSION) {
|
||||
Bundle result = new Bundle();
|
||||
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.putParcelable(OpenPgpConstants.RESULT_ERRORS, error);
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||
return result;
|
||||
}
|
||||
|
||||
// check if caller is allowed to access openpgp keychain
|
||||
Bundle result = isAllowed(params);
|
||||
Intent result = isAllowed(data);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
@ -494,61 +422,30 @@ public class OpenPgpService extends RemoteService {
|
||||
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
|
||||
|
||||
@Override
|
||||
public Bundle sign(Bundle params, final ParcelFileDescriptor input, final ParcelFileDescriptor output) {
|
||||
public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) {
|
||||
Intent errorResult = checkRequirements(data);
|
||||
if (errorResult != null) {
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
final AppSettings appSettings = getAppSettings();
|
||||
|
||||
Bundle errorResult = checkRequirements(params);
|
||||
if (errorResult != null) {
|
||||
return errorResult;
|
||||
String action = data.getAction();
|
||||
if (OpenPgpApi.ACTION_SIGN.equals(action)) {
|
||||
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_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_GET_KEY.equals(action)) {
|
||||
return getKeyImpl(data);
|
||||
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
|
||||
return getKeyIdsImpl(data);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return signImpl(params, input, output, appSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle encrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
|
||||
final AppSettings appSettings = getAppSettings();
|
||||
|
||||
Bundle errorResult = checkRequirements(params);
|
||||
if (errorResult != null) {
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
return encryptAndSignImpl(params, input, output, appSettings, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle signAndEncrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
|
||||
final AppSettings appSettings = getAppSettings();
|
||||
|
||||
Bundle errorResult = checkRequirements(params);
|
||||
if (errorResult != null) {
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
return encryptAndSignImpl(params, input, output, appSettings, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle decryptAndVerify(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
|
||||
final AppSettings appSettings = getAppSettings();
|
||||
|
||||
Bundle errorResult = checkRequirements(params);
|
||||
if (errorResult != null) {
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
return decryptAndVerifyImpl(params, input, output, appSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getKeyIds(Bundle params) {
|
||||
Bundle errorResult = checkRequirements(params);
|
||||
if (errorResult != null) {
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
return getKeyIdsImpl(params);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
import org.openintents.openpgp.util.OpenPgpConstants;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
@ -38,10 +38,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.Signature;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
|
||||
/**
|
||||
* Abstract service class for remote APIs that handle app registration and user input.
|
||||
@ -57,7 +53,7 @@ public abstract class RemoteService extends Service {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
protected Bundle isAllowed(Bundle params) {
|
||||
protected Intent isAllowed(Intent data) {
|
||||
try {
|
||||
if (isCallerAllowed(false)) {
|
||||
|
||||
@ -74,9 +70,9 @@ public abstract class RemoteService extends Service {
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(Constants.TAG, "Should not happen, returning!", e);
|
||||
// return error
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||
return result;
|
||||
}
|
||||
@ -86,14 +82,14 @@ public abstract class RemoteService extends Service {
|
||||
intent.setAction(RemoteServiceActivity.ACTION_REGISTER);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
|
||||
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||
|
||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_REGISTER, intent, 0);
|
||||
|
||||
// return PendingIntent to be executed by client
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -103,14 +99,14 @@ public abstract class RemoteService extends Service {
|
||||
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, getString(R.string.api_error_wrong_signature));
|
||||
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||
|
||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_ERROR, intent, 0);
|
||||
|
||||
// return PendingIntent to be executed by client
|
||||
Bundle result = new Bundle();
|
||||
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import android.os.Messenger;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.view.View;
|
||||
|
||||
import org.openintents.openpgp.util.OpenPgpConstants;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Id;
|
||||
@ -51,6 +51,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
|
||||
public static final String EXTRA_MESSENGER = "messenger";
|
||||
|
||||
public static final String EXTRA_DATA = "data";
|
||||
|
||||
// passphrase action
|
||||
public static final String EXTRA_SECRET_KEY_ID = "secret_key_id";
|
||||
// register action
|
||||
@ -100,12 +102,9 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
ProviderHelper.insertApiApp(RemoteServiceActivity.this,
|
||||
mSettingsFragment.getAppSettings());
|
||||
|
||||
// give params through for new service call
|
||||
Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
|
||||
|
||||
Intent finishIntent = new Intent();
|
||||
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, oldParams);
|
||||
RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
|
||||
// give data through for new service call
|
||||
Intent resultData = extras.getParcelable(EXTRA_DATA);
|
||||
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
||||
RemoteServiceActivity.this.finish();
|
||||
}
|
||||
}
|
||||
@ -128,9 +127,9 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
mSettingsFragment.setAppSettings(settings);
|
||||
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
|
||||
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
|
||||
Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
|
||||
Intent resultData = extras.getParcelable(EXTRA_DATA);
|
||||
|
||||
showPassphraseDialog(oldParams, secretKeyId);
|
||||
showPassphraseDialog(resultData, secretKeyId);
|
||||
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
|
||||
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
|
||||
ArrayList<String> missingUserIds = intent
|
||||
@ -167,13 +166,11 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// add key ids to params Bundle for new request
|
||||
Bundle params = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
|
||||
params.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS,
|
||||
Intent resultData = extras.getParcelable(EXTRA_DATA);
|
||||
resultData.putExtra(OpenPgpApi.EXTRA_KEY_IDS,
|
||||
mSelectFragment.getSelectedMasterKeyIds());
|
||||
|
||||
Intent finishIntent = new Intent();
|
||||
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||
RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
|
||||
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
||||
RemoteServiceActivity.this.finish();
|
||||
}
|
||||
}, R.string.btn_do_not_save, new View.OnClickListener() {
|
||||
@ -222,7 +219,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
RemoteServiceActivity.this.setResult(RESULT_OK);
|
||||
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
|
||||
RemoteServiceActivity.this.finish();
|
||||
}
|
||||
});
|
||||
@ -244,16 +241,14 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
* encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
|
||||
* for a symmetric passphrase
|
||||
*/
|
||||
private void showPassphraseDialog(final Bundle params, long secretKeyId) {
|
||||
private void showPassphraseDialog(final Intent data, long secretKeyId) {
|
||||
// Message is received after passphrase is cached
|
||||
Handler returnHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
|
||||
// return given params again, for calling the service method again
|
||||
Intent finishIntent = new Intent();
|
||||
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||
RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
|
||||
RemoteServiceActivity.this.setResult(RESULT_OK, data);
|
||||
} else {
|
||||
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
|
||||
}
|
||||
@ -273,9 +268,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
||||
} catch (PgpGeneralException e) {
|
||||
Log.d(Constants.TAG, "No passphrase for this secret key, do pgp operation directly!");
|
||||
// return given params again, for calling the service method again
|
||||
Intent finishIntent = new Intent();
|
||||
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||
setResult(RESULT_OK, finishIntent);
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -66,6 +68,7 @@ import android.widget.Toast;
|
||||
import android.widget.ViewFlipper;
|
||||
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
import com.devspark.appmsg.AppMsg;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public class DecryptActivity extends DrawerActivity {
|
||||
@ -233,7 +236,7 @@ public class DecryptActivity extends DrawerActivity {
|
||||
if (matcher.matches()) {
|
||||
data = matcher.group(1);
|
||||
mMessage.setText(data);
|
||||
Toast.makeText(this, R.string.using_clipboard_content, Toast.LENGTH_SHORT)
|
||||
AppMsg.makeText(this, R.string.using_clipboard_content, AppMsg.STYLE_INFO)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
@ -420,17 +423,17 @@ public class DecryptActivity extends DrawerActivity {
|
||||
}
|
||||
|
||||
if (mInputFilename.equals("")) {
|
||||
Toast.makeText(this, R.string.no_file_selected, Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(this, R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mInputFilename.startsWith("file")) {
|
||||
File file = new File(mInputFilename);
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
Toast.makeText(
|
||||
AppMsg.makeText(
|
||||
this,
|
||||
getString(R.string.error_message,
|
||||
getString(R.string.error_file_not_found)), Toast.LENGTH_SHORT)
|
||||
getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
@ -510,14 +513,14 @@ public class DecryptActivity extends DrawerActivity {
|
||||
inStream = getContentResolver().openInputStream(mContentUri);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(Constants.TAG, "File not found!", e);
|
||||
Toast.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
}
|
||||
} else if (mDecryptTarget == Id.target.file) {
|
||||
// check if storage is ready
|
||||
if (!FileHelper.isStorageMounted(mInputFilename)) {
|
||||
Toast.makeText(this, getString(R.string.error_external_storage_not_ready),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(this, getString(R.string.error_external_storage_not_ready),
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -525,8 +528,8 @@ public class DecryptActivity extends DrawerActivity {
|
||||
inStream = new BufferedInputStream(new FileInputStream(mInputFilename));
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(Constants.TAG, "File not found!", e);
|
||||
Toast.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
}
|
||||
} else {
|
||||
inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes());
|
||||
@ -556,8 +559,8 @@ public class DecryptActivity extends DrawerActivity {
|
||||
mAssumeSymmetricEncryption = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,8 +658,8 @@ public class DecryptActivity extends DrawerActivity {
|
||||
mSignatureKeyId = 0;
|
||||
mSignatureLayout.setVisibility(View.GONE);
|
||||
|
||||
Toast.makeText(DecryptActivity.this, R.string.decryption_successful,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(DecryptActivity.this, R.string.decryption_successful,
|
||||
AppMsg.STYLE_INFO).show();
|
||||
if (mReturnResult) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtras(returnData);
|
||||
@ -689,11 +692,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) {
|
||||
@ -706,19 +713,32 @@ 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);
|
||||
Toast.makeText(DecryptActivity.this,
|
||||
R.string.unknown_signature,
|
||||
Toast.LENGTH_LONG).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);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
@ -52,6 +53,7 @@ public class DrawerActivity extends ActionBarActivity {
|
||||
private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class,
|
||||
EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
|
||||
KeyListSecretActivity.class, RegisteredAppsListActivity.class };
|
||||
private Class mSelectedItem;
|
||||
|
||||
private static final int MENU_ID_PREFERENCE = 222;
|
||||
private static final int MENU_ID_HELP = 223;
|
||||
@ -94,6 +96,17 @@ public class DrawerActivity extends ActionBarActivity {
|
||||
getSupportActionBar().setTitle(mTitle);
|
||||
// creates call to onPrepareOptionsMenu()
|
||||
supportInvalidateOptionsMenu();
|
||||
|
||||
// call intent activity if selected
|
||||
if(mSelectedItem != null) {
|
||||
finish();
|
||||
overridePendingTransition(0, 0);
|
||||
|
||||
Intent intent = new Intent(DrawerActivity.this, mSelectedItem);
|
||||
startActivity(intent);
|
||||
// disable animation of activity start
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void onDrawerOpened(View drawerView) {
|
||||
@ -182,14 +195,8 @@ public class DrawerActivity extends ActionBarActivity {
|
||||
mDrawerList.setItemChecked(position, true);
|
||||
// setTitle(mDrawerTitles[position]);
|
||||
mDrawerLayout.closeDrawer(mDrawerList);
|
||||
|
||||
finish();
|
||||
overridePendingTransition(0, 0);
|
||||
|
||||
Intent intent = new Intent(this, mItemsClass[position]);
|
||||
startActivity(intent);
|
||||
// disable animation of activity start
|
||||
overridePendingTransition(0, 0);
|
||||
// set selected class
|
||||
mSelectedItem = mItemsClass[position];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@ -97,7 +98,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
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;
|
||||
@ -105,7 +106,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
private boolean mIsBrandNewKeyring = false;
|
||||
private MenuItem mSaveButton;
|
||||
|
||||
private BootstrapButton mChangePassPhrase;
|
||||
private BootstrapButton mChangePassphrase;
|
||||
|
||||
private CheckBox mNoPassphrase;
|
||||
|
||||
@ -174,7 +175,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
private void handleActionCreateKey(Intent intent) {
|
||||
Bundle extras = intent.getExtras();
|
||||
|
||||
mCurrentPassPhrase = "";
|
||||
mCurrentPassphrase = "";
|
||||
mIsBrandNewKeyring = true;
|
||||
|
||||
if (extras != null) {
|
||||
@ -190,7 +191,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
if (noPassphrase) {
|
||||
// check "no passphrase" checkbox and remove button
|
||||
mNoPassphrase.setChecked(true);
|
||||
mChangePassPhrase.setVisibility(View.GONE);
|
||||
mChangePassphrase.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,19 +201,31 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
if (generateDefaultKeys) {
|
||||
|
||||
// Send all information needed to service generate keys in other thread
|
||||
Intent serviceIntent = new Intent(this, KeychainIntentService.class);
|
||||
final Intent serviceIntent = new Intent(this, KeychainIntentService.class);
|
||||
serviceIntent.setAction(KeychainIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS);
|
||||
|
||||
// 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) {
|
||||
this, R.string.progress_generating, ProgressDialog.STYLE_SPINNER, true,
|
||||
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
// Stop key generation on cancel
|
||||
stopService(serviceIntent);
|
||||
EditKeyActivity.this.setResult(Activity.RESULT_CANCELED);
|
||||
EditKeyActivity.this.finish();
|
||||
}
|
||||
}) {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard ApgHandler first
|
||||
super.handleMessage(message);
|
||||
@ -286,7 +299,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
|
||||
mCurrentPassPhrase = PassphraseCacheService.getCachedPassphrase(
|
||||
mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(
|
||||
EditKeyActivity.this, masterKeyId);
|
||||
finallySaveClicked();
|
||||
}
|
||||
@ -393,14 +406,14 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentPassPhrase = "";
|
||||
mCurrentPassphrase = "";
|
||||
|
||||
buildLayout(false);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -431,7 +444,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
// set title based on isPassphraseSet()
|
||||
int title;
|
||||
if (isPassphraseSet()) {
|
||||
title = R.string.title_change_pass_phrase;
|
||||
title = R.string.title_change_passphrase;
|
||||
} else {
|
||||
title = R.string.title_set_passphrase;
|
||||
}
|
||||
@ -451,7 +464,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
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
|
||||
@ -473,7 +486,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
|
||||
updatePassPhraseButtonText();
|
||||
|
||||
mChangePassPhrase.setOnClickListener(new OnClickListener() {
|
||||
mChangePassphrase.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
showSetPassphraseDialog();
|
||||
}
|
||||
@ -488,10 +501,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
// remove passphrase
|
||||
mSavedNewPassPhrase = mNewPassPhrase;
|
||||
mNewPassPhrase = "";
|
||||
mChangePassPhrase.setVisibility(View.GONE);
|
||||
mChangePassphrase.setVisibility(View.GONE);
|
||||
} else {
|
||||
mNewPassPhrase = mSavedNewPassPhrase;
|
||||
mChangePassPhrase.setVisibility(View.VISIBLE);
|
||||
mChangePassphrase.setVisibility(View.VISIBLE);
|
||||
}
|
||||
somethingChanged();
|
||||
}
|
||||
@ -545,7 +558,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
if (passphrase == null) {
|
||||
showPassphraseDialog(masterKeyId);
|
||||
} else {
|
||||
mCurrentPassPhrase = passphrase;
|
||||
mCurrentPassphrase = passphrase;
|
||||
finallySaveClicked();
|
||||
}
|
||||
} catch (PgpGeneralException e) {
|
||||
@ -581,13 +594,14 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
saveParams.keysExpiryDates = getKeysExpiryDates(mKeysView);
|
||||
saveParams.keysUsages = getKeysUsages(mKeysView);
|
||||
saveParams.newPassPhrase = mNewPassPhrase;
|
||||
saveParams.oldPassPhrase = mCurrentPassPhrase;
|
||||
saveParams.oldPassPhrase = mCurrentPassphrase;
|
||||
saveParams.newKeys = toPrimitiveArray(mKeysView.getNewKeysArray());
|
||||
saveParams.keys = getKeys(mKeysView);
|
||||
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
|
||||
data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, masterCanSign);
|
||||
data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams);
|
||||
|
||||
@ -766,7 +780,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,7 @@ import android.widget.Toast;
|
||||
import android.widget.ViewFlipper;
|
||||
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
import com.devspark.appmsg.AppMsg;
|
||||
|
||||
public class EncryptActivity extends DrawerActivity {
|
||||
|
||||
@ -100,8 +101,8 @@ 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;
|
||||
|
||||
@ -415,17 +416,17 @@ public class EncryptActivity extends DrawerActivity {
|
||||
mOutputFilename = guessOutputFilename(mInputFilename);
|
||||
|
||||
if (mInputFilename.equals("")) {
|
||||
Toast.makeText(this, R.string.no_file_selected, Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(this, R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mInputFilename.startsWith("content")) {
|
||||
File file = new File(mInputFilename);
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
Toast.makeText(
|
||||
AppMsg.makeText(
|
||||
this,
|
||||
getString(R.string.error_message,
|
||||
getString(R.string.error_file_not_found)), Toast.LENGTH_SHORT)
|
||||
getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
@ -435,16 +436,16 @@ 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)) {
|
||||
Toast.makeText(this, R.string.passphrases_do_not_match, Toast.LENGTH_SHORT).show();
|
||||
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) {
|
||||
Toast.makeText(this, R.string.passphrase_must_not_be_empty, Toast.LENGTH_SHORT)
|
||||
AppMsg.makeText(this, R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
@ -452,13 +453,13 @@ public class EncryptActivity extends DrawerActivity {
|
||||
boolean encryptIt = (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0);
|
||||
// for now require at least one form of encryption for files
|
||||
if (!encryptIt && mEncryptTarget == Id.target.file) {
|
||||
Toast.makeText(this, R.string.select_encryption_key, Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(this, R.string.select_encryption_key, AppMsg.STYLE_ALERT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!encryptIt && mSecretKeyId == 0) {
|
||||
Toast.makeText(this, R.string.select_encryption_or_signature_key,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(this, R.string.select_encryption_or_signature_key,
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -549,11 +550,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;
|
||||
@ -618,8 +619,8 @@ public class EncryptActivity extends DrawerActivity {
|
||||
output = data.getString(KeychainIntentService.RESULT_ENCRYPTED_STRING);
|
||||
Log.d(Constants.TAG, "output: " + output);
|
||||
ClipboardReflection.copyToClipboard(EncryptActivity.this, output);
|
||||
Toast.makeText(EncryptActivity.this,
|
||||
R.string.encryption_to_clipboard_successful, Toast.LENGTH_SHORT)
|
||||
AppMsg.makeText(EncryptActivity.this,
|
||||
R.string.encryption_to_clipboard_successful, AppMsg.STYLE_INFO)
|
||||
.show();
|
||||
break;
|
||||
|
||||
@ -640,8 +641,8 @@ public class EncryptActivity extends DrawerActivity {
|
||||
break;
|
||||
|
||||
case Id.target.file:
|
||||
Toast.makeText(EncryptActivity.this, R.string.encryption_successful,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
AppMsg.makeText(EncryptActivity.this, R.string.encryption_successful,
|
||||
AppMsg.STYLE_INFO).show();
|
||||
|
||||
if (mDeleteAfter.isChecked()) {
|
||||
// Create and show dialog to delete original file
|
||||
@ -765,8 +766,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.
|
||||
|
@ -17,22 +17,9 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.nfc.NdefMessage;
|
||||
@ -51,6 +38,18 @@ import android.widget.ArrayAdapter;
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
import com.devspark.appmsg.AppMsg;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.BadImportKeyDialogFragment;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNavigationListener {
|
||||
public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY";
|
||||
public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX
|
||||
@ -236,10 +235,10 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
|
||||
* inside your Activity."
|
||||
* <p/>
|
||||
* from http://stackoverflow.com/questions/10983396/fragment-oncreateview-and-onactivitycreated-called-twice/14295474#14295474
|
||||
*
|
||||
* <p/>
|
||||
* In our case, if we start ImportKeysActivity with parameters to directly search using a fingerprint,
|
||||
* the fragment would be loaded twice resulting in the query being empty after the second load.
|
||||
*
|
||||
* <p/>
|
||||
* Our solution:
|
||||
* To prevent that a fragment will be loaded again even if it was already loaded loadNavFragment
|
||||
* checks against mCurrentNavPostition.
|
||||
@ -395,23 +394,8 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
|
||||
AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO)
|
||||
.show();
|
||||
if (bad > 0) {
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(
|
||||
ImportKeysActivity.this);
|
||||
|
||||
alert.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
alert.setTitle(R.string.warning);
|
||||
|
||||
alert.setMessage(ImportKeysActivity.this.getResources()
|
||||
.getQuantityString(R.plurals.bad_keys_encountered, bad, bad));
|
||||
|
||||
alert.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
alert.setCancelable(true);
|
||||
alert.create().show();
|
||||
BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad);
|
||||
badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,13 @@ import java.util.List;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.KeyServer;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import android.app.Activity;
|
||||
@ -42,10 +44,11 @@ import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.devspark.appmsg.AppMsg;
|
||||
|
||||
public class ImportKeysListFragment extends ListFragment implements
|
||||
LoaderManager.LoaderCallbacks<List<ImportKeysListEntry>> {
|
||||
LoaderManager.LoaderCallbacks<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
||||
private static final String ARG_DATA_URI = "uri";
|
||||
private static final String ARG_BYTES = "bytes";
|
||||
private static final String ARG_SERVER_QUERY = "query";
|
||||
@ -181,11 +184,10 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<List<ImportKeysListEntry>> onCreateLoader(int id, Bundle args) {
|
||||
public Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> onCreateLoader(int id, Bundle args) {
|
||||
switch (id) {
|
||||
case LOADER_ID_BYTES: {
|
||||
InputData inputData = getInputData(mKeyBytes, mDataUri);
|
||||
|
||||
return new ImportKeysListLoader(mActivity, inputData);
|
||||
}
|
||||
case LOADER_ID_SERVER_QUERY: {
|
||||
@ -198,15 +200,15 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<ImportKeysListEntry>> loader,
|
||||
List<ImportKeysListEntry> data) {
|
||||
public void onLoadFinished(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader,
|
||||
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) {
|
||||
// Swap the new cursor in. (The framework will take care of closing the
|
||||
// old cursor once we return.)
|
||||
|
||||
Log.d(Constants.TAG, "data: " + data);
|
||||
Log.d(Constants.TAG, "data: " + data.getResult());
|
||||
|
||||
// swap in the real data!
|
||||
mAdapter.setData(data);
|
||||
mAdapter.setData(data.getResult());
|
||||
mAdapter.notifyDataSetChanged();
|
||||
|
||||
setListAdapter(mAdapter);
|
||||
@ -222,11 +224,25 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
break;
|
||||
|
||||
case LOADER_ID_SERVER_QUERY:
|
||||
Toast.makeText(
|
||||
getActivity(), getResources().getQuantityString(R.plurals.keys_found,
|
||||
mAdapter.getCount(), mAdapter.getCount()),
|
||||
Toast.LENGTH_SHORT
|
||||
).show();
|
||||
|
||||
Exception error = data.getError();
|
||||
|
||||
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){
|
||||
AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query,
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
}else if(error instanceof KeyServer.QueryException){
|
||||
AppMsg.makeText(getActivity(), R.string.error_keyserver_query,
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
}else if(error instanceof KeyServer.TooManyResponses){
|
||||
AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses,
|
||||
AppMsg.STYLE_ALERT).show();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -235,7 +251,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<ImportKeysListEntry>> loader) {
|
||||
public void onLoaderReset(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader) {
|
||||
switch (loader.getId()) {
|
||||
case LOADER_ID_BYTES:
|
||||
// Clear the data in the adapter.
|
||||
|
@ -33,6 +33,7 @@ import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
|
||||
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
@ -45,9 +46,13 @@ 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.support.v4.view.MenuItemCompat;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.text.TextUtils;
|
||||
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;
|
||||
@ -63,23 +68,25 @@ 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 KeyListPublicFragment extends Fragment implements AdapterView.OnItemClickListener,
|
||||
public class KeyListPublicFragment extends Fragment implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
private KeyListPublicAdapter mAdapter;
|
||||
private StickyListHeadersListView mStickyList;
|
||||
|
||||
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_public_fragment, container, false);
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create);
|
||||
mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
|
||||
|
||||
@ -137,8 +144,6 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
|
||||
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();
|
||||
@ -172,13 +177,20 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
|
||||
showDeleteKeyDialog(mode, ids);
|
||||
break;
|
||||
}
|
||||
case R.id.menu_key_list_public_multi_select_all: {
|
||||
//Select all
|
||||
int localCount = mStickyList.getCount();
|
||||
for(int k = 0; k < localCount; k++) {
|
||||
mStickyList.setItemChecked(k, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
count = 0;
|
||||
mAdapter.clearSelection();
|
||||
}
|
||||
|
||||
@ -186,13 +198,11 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
|
||||
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 = mAdapter.getCurrentCheckedPosition().size();
|
||||
String keysSelected = getResources().getQuantityString(
|
||||
R.plurals.key_list_selected_keys, count, count);
|
||||
mode.setTitle(keysSelected);
|
||||
@ -231,10 +241,15 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
|
||||
// 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.buildPublicKeyRingsUri();
|
||||
|
||||
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
|
||||
@ -276,7 +291,8 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
|
||||
viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id)));
|
||||
startActivity(viewIntent);
|
||||
}
|
||||
|
||||
|
||||
@TargetApi(11)
|
||||
public void encrypt(ActionMode mode, long[] keyRingRowIds) {
|
||||
// get master key ids from row ids
|
||||
long[] keyRingIds = new long[keyRingRowIds.length];
|
||||
@ -298,6 +314,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
|
||||
*
|
||||
* @param keyRingRowIds
|
||||
*/
|
||||
@TargetApi(11)
|
||||
public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
|
||||
// Message is received after key is deleted
|
||||
Handler returnHandler = new Handler() {
|
||||
@ -332,4 +349,34 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
|
||||
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_public_search);
|
||||
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||
|
||||
// Execute this when searching
|
||||
mSearchView.setOnQueryTextListener(this);
|
||||
|
||||
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.
|
||||
String newQuery = !TextUtils.isEmpty(s) ? s : null;
|
||||
mCurQuery = newQuery;
|
||||
getLoaderManager().restartLoader(0, null, this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
@ -81,8 +82,6 @@ public class KeyListSecretFragment extends ListFragment implements
|
||||
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
|
||||
|
||||
private int count = 0;
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
android.view.MenuInflater inflater = getActivity().getMenuInflater();
|
||||
@ -112,13 +111,20 @@ public class KeyListSecretFragment extends ListFragment implements
|
||||
showDeleteKeyDialog(mode, ids);
|
||||
break;
|
||||
}
|
||||
case R.id.menu_key_list_public_multi_select_all: {
|
||||
//Select all
|
||||
int localCount = getListView().getCount();
|
||||
for(int k = 0; k < localCount; k++) {
|
||||
getListView().setItemChecked(k, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
count = 0;
|
||||
mAdapter.clearSelection();
|
||||
}
|
||||
|
||||
@ -126,13 +132,12 @@ public class KeyListSecretFragment extends ListFragment implements
|
||||
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 = getListView().getCheckedItemCount();
|
||||
String keysSelected = getResources().getQuantityString(
|
||||
R.plurals.key_list_selected_keys, count, count);
|
||||
mode.setTitle(keysSelected);
|
||||
@ -209,6 +214,7 @@ public class KeyListSecretFragment extends ListFragment implements
|
||||
*
|
||||
* @param keyRingRowIds
|
||||
*/
|
||||
@TargetApi(11)
|
||||
public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
|
||||
// Message is received after key is deleted
|
||||
Handler returnHandler = new Handler() {
|
||||
|
@ -24,24 +24,28 @@ 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.Context;
|
||||
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 +57,219 @@ public class PreferencesActivity extends PreferenceActivity {
|
||||
// actionBar.setDisplayHomeAsUpEnabled(false);
|
||||
// actionBar.setHomeButtonEnabled(false);
|
||||
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
//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 +294,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 +319,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 +337,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 +353,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 +364,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 +376,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -38,17 +38,22 @@ 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.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
@ -67,7 +72,8 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mSearchView = (EditText)getActivity().findViewById(R.id.select_public_key_search);
|
||||
mSearchView.addTextChangedListener(this);
|
||||
mSelectedMasterKeyIds = getArguments().getLongArray(ARG_PRESELECTED_KEY_IDS);
|
||||
}
|
||||
|
||||
@ -82,7 +88,6 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
mListView = getListView();
|
||||
|
||||
mListView.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));
|
||||
@ -220,10 +225,16 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements
|
||||
// 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
|
||||
@ -250,4 +261,21 @@ 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) {
|
||||
String newQuery = !TextUtils.isEmpty(editable.toString()) ? editable.toString() : null;
|
||||
mCurQuery = newQuery;
|
||||
getLoaderManager().restartLoader(0, null, this);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The AsyncTaskResultWrapper is used to wrap a result from a AsyncTask (for example: Loader).
|
||||
* You can pass the result and an exception in it if an error occurred.
|
||||
* Concept found at:
|
||||
* https://stackoverflow.com/questions/19593577/how-to-handle-errors-in-custom-asynctaskloader
|
||||
* @param <T> - Typ of the result which is wrapped
|
||||
*/
|
||||
public class AsyncTaskResultWrapper <T>{
|
||||
|
||||
private final T result;
|
||||
private final Exception error;
|
||||
|
||||
public AsyncTaskResultWrapper(T result, Exception error){
|
||||
this.result = result;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Exception getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
}
|
@ -33,12 +33,13 @@ import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
|
||||
import android.content.Context;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
|
||||
public class ImportKeysListLoader extends AsyncTaskLoader<List<ImportKeysListEntry>> {
|
||||
public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
||||
Context mContext;
|
||||
|
||||
InputData mInputData;
|
||||
|
||||
ArrayList<ImportKeysListEntry> data = new ArrayList<ImportKeysListEntry>();
|
||||
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> entryListWrapper;
|
||||
|
||||
public ImportKeysListLoader(Context context, InputData inputData) {
|
||||
super(context);
|
||||
@ -47,15 +48,18 @@ public class ImportKeysListLoader extends AsyncTaskLoader<List<ImportKeysListEnt
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportKeysListEntry> loadInBackground() {
|
||||
public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
|
||||
|
||||
entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(data, null);
|
||||
|
||||
if (mInputData == null) {
|
||||
Log.e(Constants.TAG, "Input data is null!");
|
||||
return data;
|
||||
return entryListWrapper;
|
||||
}
|
||||
|
||||
generateListOfKeyrings(mInputData);
|
||||
|
||||
return data;
|
||||
return entryListWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -77,7 +81,7 @@ public class ImportKeysListLoader extends AsyncTaskLoader<List<ImportKeysListEnt
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliverResult(List<ImportKeysListEntry> data) {
|
||||
public void deliverResult(AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) {
|
||||
super.deliverResult(data);
|
||||
}
|
||||
|
||||
@ -93,7 +97,7 @@ public class ImportKeysListLoader extends AsyncTaskLoader<List<ImportKeysListEnt
|
||||
|
||||
// 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 {
|
||||
|
||||
|
@ -26,15 +26,15 @@ import org.sufficientlysecure.keychain.util.KeyServer;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ImportKeysListServerLoader extends AsyncTaskLoader<List<ImportKeysListEntry>> {
|
||||
public class ImportKeysListServerLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
||||
Context mContext;
|
||||
|
||||
String mServerQuery;
|
||||
String mKeyServer;
|
||||
|
||||
ArrayList<ImportKeysListEntry> data = new ArrayList<ImportKeysListEntry>();
|
||||
private ArrayList<ImportKeysListEntry> entryList = new ArrayList<ImportKeysListEntry>();
|
||||
private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> entryListWrapper;
|
||||
|
||||
public ImportKeysListServerLoader(Context context, String serverQuery, String keyServer) {
|
||||
super(context);
|
||||
@ -44,15 +44,18 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<List<ImportKeysL
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportKeysListEntry> loadInBackground() {
|
||||
public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
|
||||
|
||||
entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, null);
|
||||
|
||||
if (mServerQuery == null) {
|
||||
Log.e(Constants.TAG, "mServerQuery is null!");
|
||||
return data;
|
||||
return entryListWrapper;
|
||||
}
|
||||
|
||||
queryServer(mServerQuery, mKeyServer);
|
||||
|
||||
return data;
|
||||
return entryListWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,7 +77,7 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<List<ImportKeysL
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliverResult(List<ImportKeysListEntry> data) {
|
||||
public void deliverResult(AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) {
|
||||
super.deliverResult(data);
|
||||
}
|
||||
|
||||
@ -87,13 +90,17 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<List<ImportKeysL
|
||||
ArrayList<ImportKeysListEntry> searchResult = server.search(query);
|
||||
|
||||
// add result to data
|
||||
data.addAll(searchResult);
|
||||
entryList.addAll(searchResult);
|
||||
entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, null);
|
||||
} catch (KeyServer.InsufficientQuery e) {
|
||||
Log.e(Constants.TAG, "InsufficientQuery", e);
|
||||
entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e);
|
||||
} catch (KeyServer.QueryException e) {
|
||||
Log.e(Constants.TAG, "QueryException", e);
|
||||
entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e);
|
||||
} catch (KeyServer.TooManyResponses e) {
|
||||
Log.e(Constants.TAG, "TooManyResponses", e);
|
||||
entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
|
||||
*/
|
||||
// default color
|
||||
v.setBackgroundColor(Color.TRANSPARENT);
|
||||
if (mSelection.get(position) != null) {
|
||||
if (mSelection.get(position) != null && mSelection.get(position).booleanValue()) {
|
||||
// this is a selected position, change color!
|
||||
v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2013 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.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
|
||||
public class BadImportKeyDialogFragment extends DialogFragment {
|
||||
private static final String ARG_BAD_IMPORT = "bad_import";
|
||||
|
||||
/**
|
||||
* Creates a new instance of this Bad Import Key DialogFragment
|
||||
*
|
||||
* @param bad
|
||||
* @return
|
||||
*/
|
||||
public static BadImportKeyDialogFragment newInstance(int bad) {
|
||||
BadImportKeyDialogFragment frag = new BadImportKeyDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
|
||||
args.putInt(ARG_BAD_IMPORT, bad);
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final FragmentActivity activity = getActivity();
|
||||
final int badImport = getArguments().getInt(ARG_BAD_IMPORT);
|
||||
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
||||
alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
|
||||
alert.setTitle(R.string.warning);
|
||||
alert.setMessage(activity.getResources()
|
||||
.getQuantityString(R.plurals.bad_keys_encountered, badImport, badImport));
|
||||
alert.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
alert.setCancelable(true);
|
||||
|
||||
return alert.create();
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2013 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.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import org.sufficientlysecure.keychain.Id;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Choice;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
public class CreateKeyDialogFragment extends DialogFragment {
|
||||
|
||||
public interface OnAlgorithmSelectedListener {
|
||||
public void onAlgorithmSelected(Choice algorithmChoice, int keySize);
|
||||
}
|
||||
|
||||
private static final String ARG_EDITOR_CHILD_COUNT = "child_count";
|
||||
|
||||
private int mNewKeySize;
|
||||
private Choice mNewKeyAlgorithmChoice;
|
||||
private OnAlgorithmSelectedListener mAlgorithmSelectedListener;
|
||||
|
||||
public void setOnAlgorithmSelectedListener(OnAlgorithmSelectedListener listener) {
|
||||
mAlgorithmSelectedListener = listener;
|
||||
}
|
||||
|
||||
public static CreateKeyDialogFragment newInstance(int mEditorChildCount) {
|
||||
CreateKeyDialogFragment frag = new CreateKeyDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
|
||||
args.putInt(ARG_EDITOR_CHILD_COUNT, mEditorChildCount);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final FragmentActivity context = getActivity();
|
||||
final LayoutInflater mInflater;
|
||||
|
||||
final int childCount = getArguments().getInt(ARG_EDITOR_CHILD_COUNT);
|
||||
mInflater = context.getLayoutInflater();
|
||||
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
||||
|
||||
View view = mInflater.inflate(R.layout.create_key_dialog, null);
|
||||
dialog.setView(view);
|
||||
dialog.setTitle(R.string.title_create_key);
|
||||
|
||||
boolean wouldBeMasterKey = (childCount == 0);
|
||||
|
||||
final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm);
|
||||
Vector<Choice> choices = new Vector<Choice>();
|
||||
choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString(
|
||||
R.string.dsa)));
|
||||
if (!wouldBeMasterKey) {
|
||||
choices.add(new Choice(Id.choice.algorithm.elgamal, getResources().getString(
|
||||
R.string.elgamal)));
|
||||
}
|
||||
|
||||
choices.add(new Choice(Id.choice.algorithm.rsa, getResources().getString(
|
||||
R.string.rsa)));
|
||||
|
||||
ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(context,
|
||||
android.R.layout.simple_spinner_item, choices);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
algorithm.setAdapter(adapter);
|
||||
// make RSA the default
|
||||
for (int i = 0; i < choices.size(); ++i) {
|
||||
if (choices.get(i).getId() == Id.choice.algorithm.rsa) {
|
||||
algorithm.setSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final Spinner keySize = (Spinner) view.findViewById(R.id.create_key_size);
|
||||
ArrayAdapter<CharSequence> keySizeAdapter = ArrayAdapter.createFromResource(
|
||||
context, R.array.key_size_spinner_values,
|
||||
android.R.layout.simple_spinner_item);
|
||||
keySizeAdapter
|
||||
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
keySize.setAdapter(keySizeAdapter);
|
||||
keySize.setSelection(3); // Default to 4096 for the key length
|
||||
dialog.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface di, int id) {
|
||||
di.dismiss();
|
||||
try {
|
||||
int nKeyIndex = keySize.getSelectedItemPosition();
|
||||
switch (nKeyIndex) {
|
||||
case 0:
|
||||
mNewKeySize = 512;
|
||||
break;
|
||||
case 1:
|
||||
mNewKeySize = 1024;
|
||||
break;
|
||||
case 2:
|
||||
mNewKeySize = 2048;
|
||||
break;
|
||||
case 3:
|
||||
mNewKeySize = 4096;
|
||||
break;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
mNewKeySize = 0;
|
||||
}
|
||||
|
||||
mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem();
|
||||
mAlgorithmSelectedListener.onAlgorithmSelected(mNewKeyAlgorithmChoice, mNewKeySize);
|
||||
}
|
||||
});
|
||||
|
||||
dialog.setCancelable(true);
|
||||
dialog.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface di, int id) {
|
||||
di.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return dialog.create();
|
||||
}
|
||||
|
||||
}
|
@ -61,7 +61,8 @@ public class DeleteFileDialogFragment extends DialogFragment {
|
||||
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
||||
|
||||
alert.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
|
||||
alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
|
||||
alert.setTitle(R.string.warning);
|
||||
alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFile));
|
||||
|
||||
@ -82,7 +83,7 @@ public class DeleteFileDialogFragment extends DialogFragment {
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
|
||||
ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance(
|
||||
R.string.progress_deleting_securely, ProgressDialog.STYLE_HORIZONTAL);
|
||||
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) {
|
||||
|
@ -17,17 +17,6 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.dialog;
|
||||
|
||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
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.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
@ -40,6 +29,14 @@ import android.os.RemoteException;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Id;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DeleteKeyDialogFragment extends DialogFragment {
|
||||
@ -100,7 +97,7 @@ public class DeleteKeyDialogFragment extends DialogFragment {
|
||||
builder.setMessage(R.string.key_deletion_confirmation_multi);
|
||||
}
|
||||
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
builder.setIcon(R.drawable.ic_dialog_alert_holo_light);
|
||||
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
|
||||
|
||||
@Override
|
||||
|
@ -153,7 +153,7 @@ 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;
|
||||
|
||||
@ -163,7 +163,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
||||
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);
|
||||
PassphraseCacheService.addCachedPassphrase(activity, keyId, passphrase);
|
||||
if (keyOK == false && clickSecretKey.getKeyID() != keyId) {
|
||||
PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(),
|
||||
passPhrase);
|
||||
passphrase);
|
||||
}
|
||||
|
||||
sendMessageToHandler(MESSAGE_OKAY);
|
||||
|
@ -21,28 +21,40 @@ import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.DialogInterface.OnKeyListener;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
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_STYLE = "style";
|
||||
private static final String ARG_CANCELABLE = "cancelable";
|
||||
|
||||
private OnCancelListener mOnCancelListener;
|
||||
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*
|
||||
* @param id
|
||||
* @param messageId
|
||||
* @param style
|
||||
* @param cancelable
|
||||
* @return
|
||||
*/
|
||||
public static ProgressDialogFragment newInstance(int messageId, int style) {
|
||||
public static ProgressDialogFragment newInstance(int messageId, int style, boolean cancelable,
|
||||
OnCancelListener onCancelListener) {
|
||||
ProgressDialogFragment frag = new ProgressDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_MESSAGE_ID, messageId);
|
||||
args.putInt(ARG_STYLE, style);
|
||||
args.putBoolean(ARG_CANCELABLE, cancelable);
|
||||
|
||||
frag.setArguments(args);
|
||||
frag.mOnCancelListener = onCancelListener;
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
@ -60,7 +72,6 @@ public class ProgressDialogFragment extends DialogFragment {
|
||||
/**
|
||||
* Updates progress of dialog
|
||||
*
|
||||
* @param messageId
|
||||
* @param progress
|
||||
* @param max
|
||||
*/
|
||||
@ -74,7 +85,7 @@ public class ProgressDialogFragment extends DialogFragment {
|
||||
/**
|
||||
* Updates progress of dialog
|
||||
*
|
||||
* @param messageId
|
||||
* @param message
|
||||
* @param progress
|
||||
* @param max
|
||||
*/
|
||||
@ -86,12 +97,20 @@ public class ProgressDialogFragment extends DialogFragment {
|
||||
dialog.setMax(max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
super.onCancel(dialog);
|
||||
|
||||
if (this.mOnCancelListener != null)
|
||||
this.mOnCancelListener.onCancel(dialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates dialog
|
||||
*/
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Activity activity = getActivity();
|
||||
final Activity activity = getActivity();
|
||||
|
||||
ProgressDialog dialog = new ProgressDialog(activity);
|
||||
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
@ -100,10 +119,22 @@ public class ProgressDialogFragment extends DialogFragment {
|
||||
|
||||
int messageId = getArguments().getInt(ARG_MESSAGE_ID);
|
||||
int style = getArguments().getInt(ARG_STYLE);
|
||||
boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE);
|
||||
|
||||
dialog.setMessage(getString(messageId));
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Disable the back button
|
||||
OnKeyListener keyListener = new OnKeyListener() {
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -17,28 +17,26 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.dialog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.QrCodeUtils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.util.QrCodeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ShareQrCodeDialogFragment extends DialogFragment {
|
||||
private static final String ARG_KEY_URI = "uri";
|
||||
private static final String ARG_FINGERPRINT_ONLY = "fingerprint_only";
|
||||
|
@ -35,6 +35,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;
|
||||
@ -60,6 +61,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
||||
TextView mKeyId;
|
||||
TextView mCreationDate;
|
||||
BootstrapButton mExpiryDateButton;
|
||||
GregorianCalendar mCreatedDate;
|
||||
GregorianCalendar mExpiryDate;
|
||||
GregorianCalendar mOriginalExpiryDate = null;
|
||||
CheckBox mChkCertify;
|
||||
@ -142,8 +144,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;
|
||||
@ -161,6 +167,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();
|
||||
}
|
||||
});
|
||||
@ -237,7 +258,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) {
|
||||
@ -268,6 +289,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) {
|
||||
@ -320,3 +350,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));
|
||||
}
|
||||
}
|
||||
|
@ -46,13 +46,25 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.sufficientlysecure.keychain.Id;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.CreateKeyDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
|
||||
import org.sufficientlysecure.keychain.util.Choice;
|
||||
|
||||
|
||||
|
||||
public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Editor {
|
||||
private LayoutInflater mInflater;
|
||||
private BootstrapButton mPlusButton;
|
||||
@ -252,84 +264,16 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
||||
}
|
||||
|
||||
case Id.type.key: {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
|
||||
|
||||
View view = mInflater.inflate(R.layout.create_key_dialog, null);
|
||||
dialog.setView(view);
|
||||
dialog.setTitle(R.string.title_create_key);
|
||||
|
||||
boolean wouldBeMasterKey = (mEditors.getChildCount() == 0);
|
||||
|
||||
final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm);
|
||||
Vector<Choice> choices = new Vector<Choice>();
|
||||
choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString(
|
||||
R.string.dsa)));
|
||||
if (!wouldBeMasterKey) {
|
||||
choices.add(new Choice(Id.choice.algorithm.elgamal, getResources().getString(
|
||||
R.string.elgamal)));
|
||||
}
|
||||
|
||||
choices.add(new Choice(Id.choice.algorithm.rsa, getResources().getString(
|
||||
R.string.rsa)));
|
||||
|
||||
ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getContext(),
|
||||
android.R.layout.simple_spinner_item, choices);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
algorithm.setAdapter(adapter);
|
||||
// make RSA the default
|
||||
for (int i = 0; i < choices.size(); ++i) {
|
||||
if (choices.get(i).getId() == Id.choice.algorithm.rsa) {
|
||||
algorithm.setSelection(i);
|
||||
break;
|
||||
CreateKeyDialogFragment mCreateKeyDialogFragment = CreateKeyDialogFragment.newInstance(mEditors.getChildCount());
|
||||
mCreateKeyDialogFragment.setOnAlgorithmSelectedListener(new CreateKeyDialogFragment.OnAlgorithmSelectedListener() {
|
||||
@Override
|
||||
public void onAlgorithmSelected(Choice algorithmChoice, int keySize) {
|
||||
mNewKeyAlgorithmChoice = algorithmChoice;
|
||||
mNewKeySize = keySize;
|
||||
createKey();
|
||||
}
|
||||
}
|
||||
|
||||
final Spinner keySize = (Spinner) view.findViewById(R.id.create_key_size);
|
||||
ArrayAdapter<CharSequence> keySizeAdapter = ArrayAdapter.createFromResource(
|
||||
getContext(), R.array.key_size_spinner_values,
|
||||
android.R.layout.simple_spinner_item);
|
||||
keySizeAdapter
|
||||
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
keySize.setAdapter(keySizeAdapter);
|
||||
keySize.setSelection(3); // Default to 4096 for the key length
|
||||
dialog.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface di, int id) {
|
||||
di.dismiss();
|
||||
try {
|
||||
int nKeyIndex = keySize.getSelectedItemPosition();
|
||||
switch (nKeyIndex) {
|
||||
case 0:
|
||||
mNewKeySize = 512;
|
||||
break;
|
||||
case 1:
|
||||
mNewKeySize = 1024;
|
||||
break;
|
||||
case 2:
|
||||
mNewKeySize = 2048;
|
||||
break;
|
||||
case 3:
|
||||
mNewKeySize = 4096;
|
||||
break;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
mNewKeySize = 0;
|
||||
}
|
||||
|
||||
mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem();
|
||||
createKey();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.setCancelable(true);
|
||||
dialog.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface di, int id) {
|
||||
di.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.create().show();
|
||||
});
|
||||
mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -382,7 +326,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
||||
|
||||
private void createKey() {
|
||||
// Send all information needed to service to edit key in other thread
|
||||
Intent intent = new Intent(mActivity, KeychainIntentService.class);
|
||||
final Intent intent = new Intent(mActivity, KeychainIntentService.class);
|
||||
|
||||
intent.setAction(KeychainIntentService.ACTION_GENERATE_KEY);
|
||||
|
||||
@ -390,18 +334,18 @@ 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);
|
||||
|
||||
@ -409,7 +353,12 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
||||
|
||||
// show progress dialog
|
||||
mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating,
|
||||
ProgressDialog.STYLE_SPINNER);
|
||||
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,
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2011 Thialfihar <thi@thialfihar.org>
|
||||
* Copyright (C) 2011 Senecaso
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@ -32,6 +34,7 @@ import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
@ -172,11 +175,11 @@ public class HkpKeyServer extends KeyServer {
|
||||
if (e.getCode() == 404) {
|
||||
return results;
|
||||
} else {
|
||||
if (e.getData().toLowerCase().contains("no keys found")) {
|
||||
if (e.getData().toLowerCase(Locale.US).contains("no keys found")) {
|
||||
return results;
|
||||
} else if (e.getData().toLowerCase().contains("too many")) {
|
||||
} else if (e.getData().toLowerCase(Locale.US).contains("too many")) {
|
||||
throw new TooManyResponses();
|
||||
} else if (e.getData().toLowerCase().contains("insufficient")) {
|
||||
} else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) {
|
||||
throw new InsufficientQuery();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2011 Thialfihar <thi@thialfihar.org>
|
||||
* Copyright (C) 2011 Senecaso
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@ -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_search.png
Normal file
After Width: | Height: | Size: 702 B |
BIN
OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_secure.png
Normal file
After Width: | Height: | Size: 394 B |
After Width: | Height: | Size: 507 B |
After Width: | Height: | Size: 1018 B |
BIN
OpenPGP-Keychain/src/main/res/drawable-mdpi/ic_action_search.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
OpenPGP-Keychain/src/main/res/drawable-mdpi/ic_action_secure.png
Normal file
After Width: | Height: | Size: 317 B |
After Width: | Height: | Size: 292 B |
After Width: | Height: | Size: 770 B |
After Width: | Height: | Size: 900 B |
After Width: | Height: | Size: 510 B |
After Width: | Height: | Size: 351 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 563 B |
After Width: | Height: | Size: 1.9 KiB |
@ -6,7 +6,7 @@
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
@ -26,4 +26,4 @@
|
||||
tools:layout="@layout/api_app_settings_fragment" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
@ -18,4 +18,4 @@
|
||||
tools:layout="@layout/api_app_settings_fragment" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
|
@ -27,7 +27,7 @@
|
||||
android:text="@string/label_no_passphrase" />
|
||||
|
||||
<com.beardedhen.androidbootstrap.BootstrapButton
|
||||
android:id="@+id/edit_key_btn_change_pass_phrase"
|
||||
android:id="@+id/edit_key_btn_change_passphrase"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:padding="4dp"
|
||||
|
@ -135,7 +135,7 @@
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_passPhrase"
|
||||
android:id="@+id/label_passphrase"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
@ -144,7 +144,7 @@
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/passPhrase"
|
||||
android:id="@+id/passphrase"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
@ -153,7 +153,7 @@
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_passPhraseAgain"
|
||||
android:id="@+id/label_passphraseAgain"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
@ -162,7 +162,7 @@
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/passPhraseAgain"
|
||||
android:id="@+id/passphraseAgain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
|
@ -71,8 +71,8 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/editors"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
@ -4,8 +4,16 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerHorizontal="true" >
|
||||
|
||||
<EditText
|
||||
android:id="@+id/select_public_key_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/menu_search"
|
||||
android:drawableLeft="@drawable/ic_action_search"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/select_public_key_fragment_container"
|
||||
android:layout_below="@id/select_public_key_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
@ -11,5 +11,10 @@
|
||||
android:id="@+id/menu_key_list_public_export"
|
||||
app:showAsAction="never"
|
||||
android:title="@string/menu_export_keys" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_key_list_public_search"
|
||||
android:title="@string/menu_search"
|
||||
android:icon="@drawable/ic_action_search"
|
||||
app:actionViewClass="android.support.v7.widget.SearchView"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
@ -1,8 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_key_list_public_multi_select_all"
|
||||
android:icon="@drawable/ic_action_select_all"
|
||||
android:title="@string/menu_select_all" />
|
||||
<item
|
||||
android:id="@+id/menu_key_list_public_multi_encrypt"
|
||||
android:icon="@drawable/ic_action_secure"
|
||||
android:title="@string/menu_encrypt_to" />
|
||||
<item
|
||||
android:id="@+id/menu_key_list_public_multi_delete"
|
||||
|
@ -1,6 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_key_list_public_multi_select_all"
|
||||
android:icon="@drawable/ic_action_select_all"
|
||||
android:title="@string/menu_select_all" />
|
||||
<item
|
||||
android:id="@+id/menu_key_list_public_multi_delete"
|
||||
android:icon="@drawable/ic_action_discard"
|
||||
|
@ -1,11 +1,11 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">http://sufficientlysecure.org/keychain</a></p>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">OpenPGP Keychain</a> ist eine OpenPGP-Implementierung für Android. Die Entwicklung begann als ein Fork von Android Privacy Guard (APG).</p>
|
||||
<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
|
||||
<p><a href="http://www.openkeychain.org">OpenKeychain</a> ist eine OpenPGP implementation für Android.</p>
|
||||
<p>Lizenz: GPLv3+</p>
|
||||
|
||||
<h2>Entwickler OpenPGP Keychain</h2>
|
||||
<h2>Entwickler OpenKeychain</h2>
|
||||
<ul>
|
||||
<li>Dominik Schürmann (Leitender Entwickler)</li>
|
||||
<li>Ash Hughes (crypto patches)</li>
|
||||
@ -23,9 +23,9 @@
|
||||
<h2>Bibliotheken</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Apache License v2)</li>
|
||||
<a href="http://developer.android.com/tools/support-library/index.html">Android Support Bibliothek v4</a> (Apache Lizenz v2)</li>
|
||||
<li>
|
||||
<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v7 'appcompat'</a> (Apache License v2)</li>
|
||||
<a href="http://developer.android.com/tools/support-library/index.html">Android Support Bibliothek v7 'appcompat'</a> (Apache Lizenz v2)</li>
|
||||
<li>
|
||||
<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Apache Lizenz v2)</li>
|
||||
<li>
|
||||
@ -36,6 +36,8 @@
|
||||
<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 Lizenz)</li>
|
||||
<li>
|
||||
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache Lizenz v2)</li>
|
||||
<li>
|
||||
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Bibliothek</a> (Apache Lizenz v2)</li>
|
||||
<li>Icons von <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike Lizenz 3.0)</li>
|
||||
<li>Icons von <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
|
||||
</ul>
|
||||
|
@ -16,12 +16,12 @@
|
||||
<h2>2.2</h2>
|
||||
<ul>
|
||||
<li>new design with navigation drawer</li>
|
||||
<li>new public key list design</li>
|
||||
<li>Neus Design für die Liste der öffentlichen Schlüssel</li>
|
||||
<li>new public key view</li>
|
||||
<li>bug fixes for importing of keys</li>
|
||||
<li>Fehler beim Schlüsselimport behoben</li>
|
||||
<li>key cross-certification (thanks to Ash Hughes)</li>
|
||||
<li>handle UTF-8 passwords properly (thanks to Ash Hughes)</li>
|
||||
<li>first version with new languages (thanks to the contributors on Transifex)</li>
|
||||
<li>Erste Version mit neuen Sprachen (Danke an die Mitwirkenden bei Transifex)</li>
|
||||
<li>sharing of keys via QR Codes fixed and improved</li>
|
||||
<li>package signature verification for API</li>
|
||||
</ul>
|
||||
@ -31,16 +31,16 @@
|
||||
</ul>
|
||||
<h2>2.1</h2>
|
||||
<ul>
|
||||
<li>lots of bug fixes</li>
|
||||
<li>new API for developers</li>
|
||||
<li>PRNG bug fix by Google</li>
|
||||
<li>Viele Fehler behoben</li>
|
||||
<li>Neue API für Entwickler</li>
|
||||
<li>PRNG Bugfix von Google</li>
|
||||
</ul>
|
||||
<h2>2.0</h2>
|
||||
<ul>
|
||||
<li>complete redesign</li>
|
||||
<li>Komlett neu designd</li>
|
||||
<li>share public keys via qr codes, nfc beam</li>
|
||||
<li>sign keys</li>
|
||||
<li>upload keys to server</li>
|
||||
<li>Schlüssel signieren</li>
|
||||
<li>Schlüssel auf den Server hochladen</li>
|
||||
<li>fixes import issues</li>
|
||||
<li>new AIDL API</li>
|
||||
</ul>
|
||||
@ -56,25 +56,25 @@
|
||||
<h2>1.0.7</h2>
|
||||
<ul>
|
||||
<li>fixed problem with signature verification of texts with trailing newline</li>
|
||||
<li>more options for pass phrase cache time to live (20, 40, 60 mins)</li>
|
||||
<li>weitere Optionen für die Time-to-live des Passphrasencaches (20, 40, 60 mins)</li>
|
||||
</ul>
|
||||
<h2>1.0.6</h2>
|
||||
<ul>
|
||||
<li>account adding crash on Froyo fixed</li>
|
||||
<li>crash beim Hinzufügen eines Kontos auf Froyo repariert</li>
|
||||
<li>sichere Dateilöschung</li>
|
||||
<li>option to delete key file after import</li>
|
||||
<li>Option, um Schlüsseldatei nach dem Import zu löschen</li>
|
||||
<li>Streamverschlüsselung/-entschlüsselung (Galerie, etc.)</li>
|
||||
<li>new options (language, force v3 signatures)</li>
|
||||
<li>interface changes</li>
|
||||
<li>neue Optionen (Sprache, v3-Unterschriften erzwingen)</li>
|
||||
<li>Interfaceänderungen</li>
|
||||
<li>Fehlerbehebungen</li>
|
||||
</ul>
|
||||
<h2>1.0.5</h2>
|
||||
<ul>
|
||||
<li>Deutsche und Italienische Übersetzung</li>
|
||||
<li>much smaller package, due to reduced BC sources</li>
|
||||
<li>new preferences GUI</li>
|
||||
<li>layout adjustment for localization</li>
|
||||
<li>signature bugfix</li>
|
||||
<li>viel kleineres Paket, dank reduzierter BC Quellen</li>
|
||||
<li>Neues Einstellungs-GUI</li>
|
||||
<li>Lay-Out-Anpassung für die Lokalisierung</li>
|
||||
<li>Fehler bei Signatur behoben</li>
|
||||
</ul>
|
||||
<h2>1.0.4</h2>
|
||||
<ul>
|
||||
@ -82,11 +82,11 @@
|
||||
</ul>
|
||||
<h2>1.0.3</h2>
|
||||
<ul>
|
||||
<li>fixed crashes during encryption/signing and possibly key export</li>
|
||||
<li>Absturz während der Verschlüsselung/Signierung und möglicherweise Schlüsselexport behoben.</li>
|
||||
</ul>
|
||||
<h2>1.0.2</h2>
|
||||
<ul>
|
||||
<li>filterable key lists</li>
|
||||
<li>Filterbare Schlüsselliste</li>
|
||||
<li>smarter pre-selection of encryption keys</li>
|
||||
<li>new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers</li>
|
||||
<li>fixes and additional features (key preselection) for K-9 Mail, new beta build available</li>
|
||||
@ -100,7 +100,7 @@
|
||||
<li>K-9 Mail integration, APG supporting beta build of K-9 Mail</li>
|
||||
<li>support of more file managers (including ASTRO)</li>
|
||||
<li>Slovenian translation</li>
|
||||
<li>new database, much faster, less memory usage</li>
|
||||
<li>Neue Datenbank, viel schneller, weniger Speicherbedarf</li>
|
||||
<li>defined Intents and content provider for other apps</li>
|
||||
<li>Fehlerbehebungen</li>
|
||||
</ul>
|
||||
|
@ -6,14 +6,14 @@
|
||||
|
||||
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
|
||||
|
||||
<h2>I found a bug in OpenPGP Keychain!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
|
||||
<h2>Ich habe einen Fehler in OpenKeychain gefunden!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
|
||||
|
||||
<h2>Contribute</h2>
|
||||
<p>If you want to help us developing OpenPGP Keychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
|
||||
<p>If you want to help us developing OpenKeychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
|
||||
|
||||
<h2>Translations</h2>
|
||||
<p>Help translating OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
|
||||
<h2>Übersetzungen</h2>
|
||||
<p>Help translating OpenKeychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain on Transifex</a>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">http://sufficientlysecure.org/keychain</a></p>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">OpenPGP Keychain</a> is an OpenPGP implementation for Android. The development began as a fork of Android Privacy Guard (APG).</p>
|
||||
<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
|
||||
<p><a href="http://www.openkeychain.org">OpenKeychain</a> is an OpenPGP implementation for Android.</p>
|
||||
<p>License: GPLv3+</p>
|
||||
|
||||
<h2>Developers OpenPGP Keychain</h2>
|
||||
<h2>Developers OpenKeychain</h2>
|
||||
<ul>
|
||||
<li>Dominik Schürmann (Lead developer)</li>
|
||||
<li>Ash Hughes (crypto patches)</li>
|
||||
@ -36,6 +36,8 @@
|
||||
<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 License)</li>
|
||||
<li>
|
||||
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
|
||||
<li>
|
||||
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
|
||||
<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
|
||||
<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
|
||||
</ul>
|
||||
|
@ -6,14 +6,14 @@
|
||||
|
||||
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
|
||||
|
||||
<h2>I found a bug in OpenPGP Keychain!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
|
||||
<h2>I found a bug in OpenKeychain!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
|
||||
|
||||
<h2>Contribute</h2>
|
||||
<p>If you want to help us developing OpenPGP Keychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
|
||||
<p>If you want to help us developing OpenKeychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
|
||||
|
||||
<h2>Translations</h2>
|
||||
<p>Help translating OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
|
||||
<p>Help translating OpenKeychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain on Transifex</a>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">http://sufficientlysecure.org/keychain</a></p>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">OpenPGP Keychain</a> es una implementación de OpenPGP para Android. Su desarrollo comenzó como un fork de Android Privacy Guard (APG).</p>
|
||||
<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
|
||||
<p><a href="http://www.openkeychain.org">OpenKeychain</a> es una implementación de OpenPGP para Android.</p>
|
||||
<p>Licencia: GPLv3+</p>
|
||||
|
||||
<h2>Desarrolladores de OpenPGP Keychain</h2>
|
||||
<h2>Desarrolladores OpenKeychain</h2>
|
||||
<ul>
|
||||
<li>Dominik Schürmann (Desarrollador principal)</li>
|
||||
<li>Ash Hughes (Parches cryptográficos)</li>
|
||||
@ -36,6 +36,8 @@
|
||||
<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (Licencia MIT X11)</li>
|
||||
<li>
|
||||
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Licencia Apache v2)</li>
|
||||
<li>
|
||||
<a href="https://github.com/johnkil/Android-AppMsg">Librería Android AppMsg</a> (Licencia Apache v2)</li>
|
||||
<li>Icons de <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Compartir-Igual licencia 3.0)</li>
|
||||
<li>Iconos de <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Dominio Público)</li>
|
||||
</ul>
|
||||
|
@ -6,14 +6,14 @@
|
||||
|
||||
<p>Es recomendable que instales <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> para una mejor selección de archivos y <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> para escanear los códigos QR generados. Pulsando en los enlaces se abrirá Google Play o F-Droid.</p>
|
||||
|
||||
<h2>¡He encontrado un bug en OpenPGP Keychain!</h2>
|
||||
<p>Por favor, informa de errores usando el <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">seguimiento de incidencias de OpenPGP Keychain</a>.</p>
|
||||
<h2>¡He encontrado un bug en OpenKeychain!</h2>
|
||||
<p>Por favor, informa de errores usando el <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">seguimiento de incidencias de OpenKeychain</a>.</p>
|
||||
|
||||
<h2>Aportar</h2>
|
||||
<p>Si quieres ayudarnos con el desarrollo de OpenPGP Keychain aportando código <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">sigue nuestra pequeña guía en Github</a>.</p>
|
||||
<p>Si quieres ayudarnos con el desarrollo de OpenKeychain aportando código <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">sigue nuestra pequeña guía en Github</a>.</p>
|
||||
|
||||
<h2>Traducciones</h2>
|
||||
<p>¡Ayúdanos a traducir OpenPGP Keychain! Todo el mundo es bienvenido en <a href="https://www.transifex.com/projects/p/openpgp-keychain/">Transifex - OpenPGP Keychain</a>.</p>
|
||||
<p>¡Ayúdanos a traducir OpenKeychain! Todo el mundo es bienvenido en <a href="https://www.transifex.com/projects/p/openpgp-keychain/">Transifex - OpenKeychain</a>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">http://sufficientlysecure.org/keychain</a></p>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">Le porte-clefs OpenPGP</a> est une implémentation d'OpenPGP pour Android. Le développement a commencé comme bifurcation d'Android Privacy Guard (APG).</p>
|
||||
<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
|
||||
<p><a href="http://www.openkeychain.org">OpenKeychain</a> est une implémentation d'OpenPGP pour Android.</p>
|
||||
<p>Licence : GPLv3+</p>
|
||||
|
||||
<h2>Les développeurs du Porte-clefs OpenPGP</h2>
|
||||
<h2>Les développeurs d'OpenKeychain</h2>
|
||||
<ul>
|
||||
<li>Dominik Schürmann (développeur principal)</li>
|
||||
<li>Ash Hughes (correctif crypto)</li>
|
||||
@ -36,6 +36,8 @@
|
||||
<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (Licence MIT X11)</li>
|
||||
<li>
|
||||
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Licence Apache v2)</li>
|
||||
<li>
|
||||
<a href="https://github.com/johnkil/Android-AppMsg">Bibliothèque Android AppMsg</a> (Licence Apache v2)</li>
|
||||
<li>Icônes du <a href="http://rrze-icon-set.berlios.de/">jeu d'icônes RRZE</a> (Licence Creative Commons Paternité - Partage des Conditions Initiales à l'Identique 3.0)</li>
|
||||
<li>Icônes du <a href="http://tango.freedesktop.org/">jeu d'icônes Tango</a> (domaine public)</li>
|
||||
</ul>
|
||||
|
@ -6,14 +6,14 @@
|
||||
|
||||
<p>Il vous est recommendé d'installer le <a href="market://details?id=org.openintents.filemanager">gestionnaire de fichiers OI</a> pour sa fonction améliorée de séléction des fichiers et le <a href="market://details?id=com.google.zxing.client.android">lecteur de codes à barres</a> pour balayer les codes QR générés. Cliquer sur les liens ouvrira Google Play Store ou F-Droid pour l'installation.</p>
|
||||
|
||||
<h2>J'ai trouvé un bogue dans le Porte-clefs OpenPGP !</h2>
|
||||
<p>Veuillez rapporter le bogue en utilisant le <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">gestionnaire de bogues du Porte-clefs OpenPGP</a>.</p>
|
||||
<h2>J'ai trouvé un bogue dans OpenKeychain !</h2>
|
||||
<p>Veuillez rapporter le bogue en utilisant le <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">gestionnaire de bogue d'OpenKeychain</a>.</p>
|
||||
|
||||
<h2>Contribuer</h2>
|
||||
<p>Si vous voulez nous aider à développer le Porte-clefs OpenPGP en y contribuant par du code, <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">veuillez suivre notre petit guide sur Github</a>.</p>
|
||||
<p>Si vous voulez nous aider à développer OpenKeychain en y contribuant par du code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">veuillez suivre notre petit guide sur Github</a>.</p>
|
||||
|
||||
<h2>Traductions</h2>
|
||||
<p>Aidez-nous à traduire le Porte-clefs OpenPGP ! Tout le monde peut y participer sur la <a href="https://www.transifex.com/projects/p/openpgp-keychain/">page Transifex du Porte-clefs OpenPGP Keychain</a>.</p>
|
||||
<p>Aidez-nous à traduire le OpenKeychain ! Tout le monde peut y participer sur la <a href="https://www.transifex.com/projects/p/openpgp-keychain/">page d'OpenKeychain sur Transifex</a>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,43 +1,45 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">http://sufficientlysecure.org/keychain</a></p>
|
||||
<p><a href="http://sufficientlysecure.org/keychain">OpenPGP Keychain</a> is an OpenPGP implementation for Android. The development began as a fork of Android Privacy Guard (APG).</p>
|
||||
<p>License: GPLv3+</p>
|
||||
<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
|
||||
<p><a href="http://www.openkeychain.org">OpenKeychain</a> un implementazione OpenPGP per Android.</p>
|
||||
<p>Licenza: GPLv3+</p>
|
||||
|
||||
<h2>Developers OpenPGP Keychain</h2>
|
||||
<h2>Sviluppatori OpenKeychain</h2>
|
||||
<ul>
|
||||
<li>Dominik Schürmann (Lead developer)</li>
|
||||
<li>Ash Hughes (crypto patches)</li>
|
||||
<li>Dominik Schürmann (Capo Sviluppatore)</li>
|
||||
<li>Ash Hughes (Patch crittografia)</li>
|
||||
<li>Brian C. Barnes</li>
|
||||
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
|
||||
<li>Bahtiar 'kalkin' Gadimov (Interfaccia Utente)</li>
|
||||
|
||||
</ul>
|
||||
<h2>Developers APG 1.x</h2>
|
||||
<h2>Sviluppatori APG 1.x</h2>
|
||||
<ul>
|
||||
<li>'Thialfihar' (Lead developer)</li>
|
||||
<li>'Senecaso' (QRCode, sign key, upload key)</li>
|
||||
<li>'Thialfihar' (Capo Sviluppatore)</li>
|
||||
<li>'Senecaso' (QRCode, firma chiavi, caricamento chiavi)</li>
|
||||
<li>Oliver Runge</li>
|
||||
<li>Markus Doits</li>
|
||||
</ul>
|
||||
<h2>Libraries</h2>
|
||||
<h2>Librerie</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Apache License v2)</li>
|
||||
<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Licenza Apache v2)</li>
|
||||
<li>
|
||||
<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v7 'appcompat'</a> (Apache License v2)</li>
|
||||
<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v7 'appcompat'</a> (Licenza Apache v2)</li>
|
||||
<li>
|
||||
<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Apache License v2)</li>
|
||||
<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Licenza Apache v2)</li>
|
||||
<li>
|
||||
<a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (MIT License)</li>
|
||||
<a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (Licenza MIT)</li>
|
||||
<li>
|
||||
<a href="http://code.google.com/p/zxing/">ZXing</a> (Apache License v2)</li>
|
||||
<a href="http://code.google.com/p/zxing/">ZXing</a> (Licenza Apache v2)</li>
|
||||
<li>
|
||||
<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 License)</li>
|
||||
<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (Licenza MIT X11)</li>
|
||||
<li>
|
||||
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
|
||||
<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
|
||||
<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
|
||||
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Licenza Apache v2)</li>
|
||||
<li>
|
||||
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Licenza Apache v2)</li>
|
||||
<li>Icone da <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Licenza Creative Commons Attribution Share-Alike 3.0)</li>
|
||||
<li>Icone da <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Pubblico Dominio)</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -3,106 +3,106 @@
|
||||
<body>
|
||||
<h2>2.3</h2>
|
||||
<ul>
|
||||
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
|
||||
<li>fix setting expiry dates on keys (thanks to Ash Hughes)</li>
|
||||
<li>more internal fixes when editing keys (thanks to Ash Hughes)</li>
|
||||
<li>querying keyservers directly from the import screen</li>
|
||||
<li>fix layout and dialog style on Android 2.2-3.0</li>
|
||||
<li>fix crash on keys with empty user ids</li>
|
||||
<li>fix crash and empty lists when coming back from signing screen</li>
|
||||
<li>Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source</li>
|
||||
<li>fix upload of key from signing screen</li>
|
||||
<li>rimossa esportazione non necessaria delle chiavi pubbliche quando si esportano le chiavi private (grazie a Ash Hughes)</li>
|
||||
<li>corretto impostazione data di scadenza delle chiavi (grazie a Ash Hughes)</li>
|
||||
<li>altre correzioni interne quando si modificano le chiavi (grazie a Ash Hughes)</li>
|
||||
<li>interrogazione server delle chiavi direttamente dalla schermata di importazione</li>
|
||||
<li>corretto layout e dialog style su Android 2.2-3.0</li>
|
||||
<li>corretto crash su chiavi con id utente vuoto</li>
|
||||
<li>corretto crash e liste vuote quando si torna dalla schermata di firma</li>
|
||||
<li>Bouncy Castle (libreria crittografica) aggiornata da 1.47 a 1.50 e compilata da sorgente</li>
|
||||
<li>corretto upload delle chiavi dalla schermata di firma</li>
|
||||
</ul>
|
||||
<h2>2.2</h2>
|
||||
<ul>
|
||||
<li>new design with navigation drawer</li>
|
||||
<li>new public key list design</li>
|
||||
<li>new public key view</li>
|
||||
<li>bug fixes for importing of keys</li>
|
||||
<li>key cross-certification (thanks to Ash Hughes)</li>
|
||||
<li>handle UTF-8 passwords properly (thanks to Ash Hughes)</li>
|
||||
<li>first version with new languages (thanks to the contributors on Transifex)</li>
|
||||
<li>sharing of keys via QR Codes fixed and improved</li>
|
||||
<li>package signature verification for API</li>
|
||||
<li>nuovo design con drawer di navigazione</li>
|
||||
<li>nuovo design per la lista chiavi pubbliche</li>
|
||||
<li>nuova visuale chiavi pubbliche</li>
|
||||
<li>correzione bug per importazione chiavi</li>
|
||||
<li>Chiave certificazione incrociata (grazie a Ash Hughes)</li>
|
||||
<li>password UTF-8 gestite correttamente (grazie a Ash Hughes)</li>
|
||||
<li>Prima versione con nuove lingue (grazie ai contributori su Transifex)</li>
|
||||
<li>condivisione di chiavi via Codici QR corretta e migliorata</li>
|
||||
<li>Verifica firma pacchetto per API</li>
|
||||
</ul>
|
||||
<h2>2.1.1</h2>
|
||||
<ul>
|
||||
<li>API Updates, preparation for K-9 Mail integration</li>
|
||||
<li>Aggiornamenti API, preparazione per integrazione con K-9 Mail</li>
|
||||
</ul>
|
||||
<h2>2.1</h2>
|
||||
<ul>
|
||||
<li>lots of bug fixes</li>
|
||||
<li>new API for developers</li>
|
||||
<li>molte correzioni di bug</li>
|
||||
<li>nuove API per sviluppatori</li>
|
||||
<li>PRNG bug fix by Google</li>
|
||||
</ul>
|
||||
<h2>2.0</h2>
|
||||
<ul>
|
||||
<li>complete redesign</li>
|
||||
<li>share public keys via qr codes, nfc beam</li>
|
||||
<li>sign keys</li>
|
||||
<li>upload keys to server</li>
|
||||
<li>fixes import issues</li>
|
||||
<li>new AIDL API</li>
|
||||
<li>completo restyle</li>
|
||||
<li>condivisione chiavi pubbliche via codici qr, nfc beam</li>
|
||||
<li>firma chiavi</li>
|
||||
<li>Carica chiavi sul server</li>
|
||||
<li>corrette caratteristiche di importazione</li>
|
||||
<li>nuova AIDL API</li>
|
||||
</ul>
|
||||
<h2>1.0.8</h2>
|
||||
<ul>
|
||||
<li>basic keyserver support</li>
|
||||
<li>supporto base per server delle chiavi</li>
|
||||
<li>app2sd</li>
|
||||
<li>more choices for pass phrase cache: 1, 2, 4, 8, hours</li>
|
||||
<li>translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)</li>
|
||||
<li>bugfixes</li>
|
||||
<li>optimizations</li>
|
||||
<li>Aggiunte opzioni per la cache della frase di accesso: 1, 2, 4, 8 ore</li>
|
||||
<li>Traduzioni: Norvegese (grazie, Sander Danielsen), Cinese (grazie, Zhang Fredrick)</li>
|
||||
<li>correzione bug</li>
|
||||
<li>ottimizzazioni</li>
|
||||
</ul>
|
||||
<h2>1.0.7</h2>
|
||||
<ul>
|
||||
<li>fixed problem with signature verification of texts with trailing newline</li>
|
||||
<li>more options for pass phrase cache time to live (20, 40, 60 mins)</li>
|
||||
<li>corretto problema con la verifica firma di testi con capo finale</li>
|
||||
<li>maggiori opzioni per il tempo di mantenimento della cache della frase di accesso (20, 40, 60 minuti)</li>
|
||||
</ul>
|
||||
<h2>1.0.6</h2>
|
||||
<ul>
|
||||
<li>account adding crash on Froyo fixed</li>
|
||||
<li>secure file deletion</li>
|
||||
<li>option to delete key file after import</li>
|
||||
<li>stream encryption/decryption (gallery, etc.)</li>
|
||||
<li>new options (language, force v3 signatures)</li>
|
||||
<li>interface changes</li>
|
||||
<li>bugfixes</li>
|
||||
<li>crash della aggiunta degli account risolto su Froyo</li>
|
||||
<li>Cancellazione sicura dei file</li>
|
||||
<li>opzione per cancellare file delle chiavi dopo l'importazione</li>
|
||||
<li>flusso codifica/decodifica (galleria, ecc.)</li>
|
||||
<li>nuove opzioni (lingua, forza firme v3)</li>
|
||||
<li>cambiamenti interfaccia</li>
|
||||
<li>correzione bug</li>
|
||||
</ul>
|
||||
<h2>1.0.5</h2>
|
||||
<ul>
|
||||
<li>German and Italian translation</li>
|
||||
<li>much smaller package, due to reduced BC sources</li>
|
||||
<li>new preferences GUI</li>
|
||||
<li>layout adjustment for localization</li>
|
||||
<li>signature bugfix</li>
|
||||
<li>Traduzione Italiana e Tedesca</li>
|
||||
<li>dimensioni pacchetto ridotte, a causa della riduzione dei sorgenti BC</li>
|
||||
<li>Nuove preferenze GUI</li>
|
||||
<li>Regolazione layout per la localizzazione</li>
|
||||
<li>correzione bug firma</li>
|
||||
</ul>
|
||||
<h2>1.0.4</h2>
|
||||
<ul>
|
||||
<li>fixed another crash caused by some SDK bug with query builder</li>
|
||||
<li>corretto altro crash causato da alcuni bug SDK con query builder</li>
|
||||
</ul>
|
||||
<h2>1.0.3</h2>
|
||||
<ul>
|
||||
<li>fixed crashes during encryption/signing and possibly key export</li>
|
||||
<li>corretto crash durante codifica/firma e possibilita' di esportare chiave</li>
|
||||
</ul>
|
||||
<h2>1.0.2</h2>
|
||||
<ul>
|
||||
<li>filterable key lists</li>
|
||||
<li>smarter pre-selection of encryption keys</li>
|
||||
<li>new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers</li>
|
||||
<li>fixes and additional features (key preselection) for K-9 Mail, new beta build available</li>
|
||||
<li>liste chiavi filtrabili</li>
|
||||
<li>preselezione di chiavi di codifica intelligente</li>
|
||||
<li>nuovo gestore intent per VIEW e SEND, permette la codifica/decodifica file all'infuori di file manager</li>
|
||||
<li>caratteristiche corrette e aggiunte (preselezione chiavi) per K-9 Mail. nuova build beta disponibile</li>
|
||||
</ul>
|
||||
<h2>1.0.1</h2>
|
||||
<ul>
|
||||
<li>GMail account listing was broken in 1.0.0, fixed again</li>
|
||||
<li>elencazione account GMail corrotta in 1.0.0, corretta nuovamente</li>
|
||||
</ul>
|
||||
<h2>1.0.0</h2>
|
||||
<ul>
|
||||
<li>K-9 Mail integration, APG supporting beta build of K-9 Mail</li>
|
||||
<li>support of more file managers (including ASTRO)</li>
|
||||
<li>Slovenian translation</li>
|
||||
<li>new database, much faster, less memory usage</li>
|
||||
<li>defined Intents and content provider for other apps</li>
|
||||
<li>bugfixes</li>
|
||||
<li>integrazione K-9 Mail, APG supporto beta build di K-9 Mail</li>
|
||||
<li>supporto per altri file manager (incluso ASTRO)</li>
|
||||
<li>traduzione Slovena</li>
|
||||
<li>Nuovo database, piu' veloce, utilizzo memoria ridotto</li>
|
||||
<li>Definiti Intent e ContentProvider per le altre app</li>
|
||||
<li>correzione bug</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|