Update openpgp-api-library for APIv7

This commit is contained in:
Dominik Schürmann 2015-03-16 14:29:12 +01:00
parent bf344dee5d
commit e4cfd3c886
9 changed files with 521 additions and 140 deletions

View File

@ -45,7 +45,7 @@ import com.fsck.k9.mailstore.LocalFolder;
import com.fsck.k9.mailstore.StorageManager;
import com.fsck.k9.service.MailService;
import org.openintents.openpgp.util.OpenPgpListPreference;
import org.openintents.openpgp.util.OpenPgpAppPreference;
import org.openintents.openpgp.util.OpenPgpUtils;
@ -174,7 +174,7 @@ public class AccountSettings extends K9PreferenceActivity {
private ListPreference mIdleRefreshPeriod;
private ListPreference mMaxPushFolders;
private boolean mHasCrypto = false;
private OpenPgpListPreference mCryptoApp;
private OpenPgpAppPreference mCryptoApp;
private PreferenceScreen mSearchScreen;
private CheckBoxPreference mCloudSearchEnabled;
@ -687,7 +687,7 @@ public class AccountSettings extends K9PreferenceActivity {
mHasCrypto = OpenPgpUtils.isAvailable(this);
if (mHasCrypto) {
mCryptoApp = (OpenPgpListPreference) findPreference(PREFERENCE_CRYPTO_APP);
mCryptoApp = (OpenPgpAppPreference) findPreference(PREFERENCE_CRYPTO_APP);
mCryptoApp.setValue(String.valueOf(mAccount.getCryptoApp()));
mCryptoApp.setSummary(mCryptoApp.getEntry());

View File

@ -285,7 +285,7 @@ public class OpenPgpHeaderView extends LinearLayout {
}
private void setUserId(OpenPgpSignatureResult signatureResult) {
final OpenPgpUtils.UserInfo userInfo = OpenPgpUtils.splitUserId(signatureResult.getPrimaryUserId());
final OpenPgpUtils.UserId userInfo = OpenPgpUtils.splitUserId(signatureResult.getPrimaryUserId());
if (userInfo.name != null) {
resultSignatureName.setText(userInfo.name);
} else {

View File

@ -466,7 +466,7 @@
android:title="@string/account_settings_crypto"
android:key="crypto">
<org.openintents.openpgp.util.OpenPgpListPreference
<org.openintents.openpgp.util.OpenPgpAppPreference
android:persistent="false"
android:key="crypto_app"
android:title="@string/account_settings_crypto_app" />

View File

@ -1,12 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.openintents.openpgp"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="19" />
package="org.openintents.openpgp">
<application/>

View File

@ -57,8 +57,15 @@ public class OpenPgpApi {
* - New extra for ACTION_DETACHED_SIGN: EXTRA_DETACHED_SIGNATURE
* - New result for ACTION_DECRYPT_VERIFY: RESULT_DETACHED_SIGNATURE
* - New result for ACTION_DECRYPT_VERIFY: RESULT_CHARSET
* 7:
* - Deprecation of ACCOUNT_NAME, please use ACTION_GET_SIGN_KEY_ID to get key id
* - Introduce EXTRA_SIGN_KEY_ID
* - New extra for ACTION_ENCRYPT and ACTION_SIGN_AND_ENCRYPT: EXTRA_ENABLE_COMPRESSION (default to true)
* - Return PendingIntent to view key for signatures
* - New result for ACTION_DECRYPT_VERIFY: RESULT_TYPE
* - New ACTION_GET_SIGN_KEY_ID
*/
public static final int API_VERSION = 6;
public static final int API_VERSION = 7;
/**
* General extras
@ -90,6 +97,9 @@ public class OpenPgpApi {
* - end cleartext with newline
* - remove whitespaces on line endings
* <p/>
* required extras:
* long EXTRA_SIGN_KEY_ID (key id of signing key)
* <p/>
* optional extras:
* String EXTRA_PASSPHRASE (key passphrase)
*/
@ -100,6 +110,9 @@ public class OpenPgpApi {
* No OutputStream necessary for ACTION_DETACHED_SIGN (No magic pre-processing like in ACTION_CLEARTEXT_SIGN)!
* The detached signature is returned separately in RESULT_DETACHED_SIGNATURE.
* <p/>
* required extras:
* long EXTRA_SIGN_KEY_ID (key id of signing key)
* <p/>
* optional extras:
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for detached signature)
* String EXTRA_PASSPHRASE (key passphrase)
@ -121,6 +134,7 @@ public class OpenPgpApi {
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for output)
* String EXTRA_PASSPHRASE (key passphrase)
* String EXTRA_ORIGINAL_FILENAME (original filename to be encrypted as metadata)
* boolean EXTRA_ENABLE_COMPRESSION (enable ZLIB compression, default ist true)
*/
public static final String ACTION_ENCRYPT = "org.openintents.openpgp.action.ENCRYPT";
@ -133,9 +147,11 @@ public class OpenPgpApi {
* long[] EXTRA_KEY_IDS
* <p/>
* optional extras:
* long EXTRA_SIGN_KEY_ID (key id of signing key)
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for output)
* String EXTRA_PASSPHRASE (key passphrase)
* String EXTRA_ORIGINAL_FILENAME (original filename to be encrypted as metadata)
* boolean EXTRA_ENABLE_COMPRESSION (enable ZLIB compression, default ist true)
*/
public static final String ACTION_SIGN_AND_ENCRYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT";
@ -146,6 +162,8 @@ public class OpenPgpApi {
* <p/>
* If OpenPgpSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING
* in addition a PendingIntent is returned via RESULT_INTENT to download missing keys.
* On all other status, in addition a PendingIntent is returned via RESULT_INTENT to open
* the key view in OpenKeychain.
* <p/>
* optional extras:
* byte[] EXTRA_DETACHED_SIGNATURE (detached signature)
@ -154,6 +172,7 @@ public class OpenPgpApi {
* OpenPgpSignatureResult RESULT_SIGNATURE
* OpenPgpDecryptMetadata RESULT_METADATA
* String RESULT_CHARSET (charset which was specified in the headers of ascii armored input, if any)
* int RESULT_TYPE
*/
public static final String ACTION_DECRYPT_VERIFY = "org.openintents.openpgp.action.DECRYPT_VERIFY";
@ -168,6 +187,17 @@ public class OpenPgpApi {
*/
public static final String ACTION_DECRYPT_METADATA = "org.openintents.openpgp.action.DECRYPT_METADATA";
/**
* Select key id for signing
* <p/>
* optional extras:
* String EXTRA_USER_ID
* <p/>
* returned extras:
* long EXTRA_SIGN_KEY_ID
*/
public static final String ACTION_GET_SIGN_KEY_ID = "org.openintents.openpgp.action.GET_SIGN_KEY_ID";
/**
* Get key ids based on given user ids (=emails)
* <p/>
@ -191,9 +221,11 @@ public class OpenPgpApi {
*/
public static final String ACTION_GET_KEY = "org.openintents.openpgp.action.GET_KEY";
/* Intent extras */
public static final String EXTRA_API_VERSION = "api_version";
// DEPRECATED!!!
public static final String EXTRA_ACCOUNT_NAME = "account_name";
// ACTION_DETACHED_SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY
@ -207,15 +239,20 @@ public class OpenPgpApi {
// ENCRYPT, SIGN_AND_ENCRYPT
public static final String EXTRA_USER_IDS = "user_ids";
public static final String EXTRA_KEY_IDS = "key_ids";
public static final String EXTRA_SIGN_KEY_ID = "sign_key_id";
// optional extras:
public static final String EXTRA_PASSPHRASE = "passphrase";
public static final String EXTRA_ORIGINAL_FILENAME = "original_filename";
public static final String EXTRA_ENABLE_COMPRESSION = "enable_compression";
// internal NFC states
public static final String EXTRA_NFC_SIGNED_HASH = "nfc_signed_hash";
public static final String EXTRA_NFC_SIG_CREATION_TIMESTAMP = "nfc_sig_creation_timestamp";
public static final String EXTRA_NFC_DECRYPTED_SESSION_KEY = "nfc_decrypted_session_key";
// GET_SIGN_KEY_ID
public static final String EXTRA_USER_ID = "user_id";
// GET_KEY
public static final String EXTRA_KEY_ID = "key_id";
public static final String RESULT_KEY_IDS = "key_ids";
@ -236,12 +273,16 @@ public class OpenPgpApi {
// DECRYPT_VERIFY
public static final String EXTRA_DETACHED_SIGNATURE = "detached_signature";
public static final String RESULT_SIGNATURE = "signature";
public static final String RESULT_METADATA = "metadata";
// This will be the charset which was specified in the headers of ascii armored input, if any
public static final String RESULT_CHARSET = "charset";
public static final String RESULT_TYPE = "type";
public static final int RESULT_TYPE_UNENCRYPTED_UNSIGNED = 0;
public static final int RESULT_TYPE_ENCRYPTED = 1;
public static final int RESULT_TYPE_SIGNED = 2;
IOpenPgpService mService;
Context mContext;

View File

@ -25,12 +25,15 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.preference.DialogPreference;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.TextView;
import org.openintents.openpgp.R;
import java.util.ArrayList;
@ -40,7 +43,7 @@ import java.util.List;
* Does not extend ListPreference, but is very similar to it!
* http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/preference/ListPreference.java/?v=source
*/
public class OpenPgpListPreference extends DialogPreference {
public class OpenPgpAppPreference extends DialogPreference {
private static final String OPENKEYCHAIN_PACKAGE = "org.sufficientlysecure.keychain";
private static final String MARKET_INTENT_URI_BASE = "market://details?id=%s";
private static final Intent MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse(
@ -53,16 +56,17 @@ public class OpenPgpListPreference extends DialogPreference {
PROVIDER_BLACKLIST.add("org.thialfihar.android.apg");
}
private ArrayList<OpenPgpProviderEntry> mLegacyList = new ArrayList<OpenPgpProviderEntry>();
private ArrayList<OpenPgpProviderEntry> mList = new ArrayList<OpenPgpProviderEntry>();
private ArrayList<OpenPgpProviderEntry> mLegacyList = new ArrayList<>();
private ArrayList<OpenPgpProviderEntry> mList = new ArrayList<>();
private String mSelectedPackage;
public OpenPgpListPreference(Context context, AttributeSet attrs) {
public OpenPgpAppPreference(Context context, AttributeSet attrs) {
super(context, attrs);
populateAppList();
}
public OpenPgpListPreference(Context context) {
public OpenPgpAppPreference(Context context) {
this(context, null);
}
@ -79,18 +83,177 @@ public class OpenPgpListPreference extends DialogPreference {
@Override
protected void onPrepareDialogBuilder(Builder builder) {
// do again, maybe an app has now been installed
populateAppList();
// Init ArrayAdapter with OpenPGP Providers
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
android.R.layout.select_dialog_singlechoice, android.R.id.text1, mList) {
public View getView(int position, View convertView, ViewGroup parent) {
// User super class to create the View
View v = super.getView(position, convertView, parent);
TextView tv = (TextView) v.findViewById(android.R.id.text1);
// Put the image on the TextView
tv.setCompoundDrawablesWithIntrinsicBounds(mList.get(position).icon, null,
null, null);
// Add margin between image and text (support various screen densities)
int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
tv.setCompoundDrawablePadding(dp10);
return v;
}
};
builder.setSingleChoiceItems(adapter, getIndexOfProviderList(mSelectedPackage),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
OpenPgpProviderEntry entry = mList.get(which);
if (entry.intent != null) {
/*
* Intents are called as activity
*
* Current approach is to assume the user installed the app.
* If he does not, the selected package is not valid.
*
* However applications should always consider this could happen,
* as the user might remove the currently used OpenPGP app.
*/
getContext().startActivity(entry.intent);
return;
}
mSelectedPackage = entry.packageName;
/*
* Clicking on an item simulates the positive button click, and dismisses
* the dialog.
*/
OpenPgpAppPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
dialog.dismiss();
}
});
/*
* The typical interaction for list-based dialogs is to have click-on-an-item dismiss the
* dialog instead of the user having to press 'Ok'.
*/
builder.setPositiveButton(null, null);
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult && (mSelectedPackage != null)) {
save();
}
}
private void save() {
// Give the client a chance to ignore this change if they deem it
// invalid
if (!callChangeListener(mSelectedPackage)) {
// They don't want the value to be set
return;
}
setAndPersist(mSelectedPackage);
}
private void setAndPersist(String packageName) {
mSelectedPackage = packageName;
// Save to persistent storage (this method will make sure this
// preference should be persistent, along with other useful checks)
persistString(mSelectedPackage);
// Data has changed, notify so UI can be refreshed!
notifyChanged();
// also update summary with selected provider
updateSummary(mSelectedPackage);
}
private void updateSummary(String packageName) {
String summary = getEntryByValue(packageName);
setSummary(summary);
}
@Override
public CharSequence getSummary() {
return getEntryByValue(mSelectedPackage);
}
private int getIndexOfProviderList(String packageName) {
for (OpenPgpProviderEntry app : mList) {
if (app.packageName.equals(packageName)) {
return mList.indexOf(app);
}
}
// default is "none"
return 0;
}
public String getEntry() {
return getEntryByValue(mSelectedPackage);
}
public String getValue() {
return mSelectedPackage;
}
public void setValue(String packageName) {
setAndPersist(packageName);
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
if (restoreValue) {
// Restore state
mSelectedPackage = getPersistedString(mSelectedPackage);
updateSummary(mSelectedPackage);
} else {
String value = (String) defaultValue;
setAndPersist(value);
updateSummary(value);
}
}
public String getEntryByValue(String packageName) {
for (OpenPgpProviderEntry app : mList) {
if (app.packageName.equals(packageName) && app.intent == null) {
return app.simpleName;
}
}
return getContext().getString(R.string.openpgp_list_preference_none);
}
private void populateAppList() {
mList.clear();
// add "none"-entry
mList.add(0, new OpenPgpProviderEntry("",
getContext().getString(R.string.openpgp_list_preference_none),
getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize)));
// add all additional (legacy) providers
mList.addAll(mLegacyList);
// search for OpenPGP providers...
ArrayList<OpenPgpProviderEntry> providerList = new ArrayList<OpenPgpProviderEntry>();
ArrayList<OpenPgpProviderEntry> providerList = new ArrayList<>();
Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT);
List<ResolveInfo> resInfo = getContext().getPackageManager().queryIntentServices(intent, 0);
if (!resInfo.isEmpty()) {
@ -128,117 +291,6 @@ public class OpenPgpListPreference extends DialogPreference {
// add provider
mList.addAll(providerList);
}
// Init ArrayAdapter with OpenPGP Providers
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
android.R.layout.select_dialog_singlechoice, android.R.id.text1, mList) {
public View getView(int position, View convertView, ViewGroup parent) {
// User super class to create the View
View v = super.getView(position, convertView, parent);
TextView tv = (TextView) v.findViewById(android.R.id.text1);
// Put the image on the TextView
tv.setCompoundDrawablesWithIntrinsicBounds(mList.get(position).icon, null,
null, null);
// Add margin between image and text (support various screen densities)
int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
tv.setCompoundDrawablePadding(dp10);
return v;
}
};
builder.setSingleChoiceItems(adapter, getIndexOfProviderList(getValue()),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
OpenPgpProviderEntry entry = mList.get(which);
if (entry.intent != null) {
/*
* Intents are called as activity
*
* Current approach is to assume the user installed the app.
* If he does not, the selected package is not valid.
*
* However applications should always consider this could happen,
* as the user might remove the currently used OpenPGP app.
*/
getContext().startActivity(entry.intent);
}
mSelectedPackage = entry.packageName;
/*
* Clicking on an item simulates the positive button click, and dismisses
* the dialog.
*/
OpenPgpListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
dialog.dismiss();
}
});
/*
* The typical interaction for list-based dialogs is to have click-on-an-item dismiss the
* dialog instead of the user having to press 'Ok'.
*/
builder.setPositiveButton(null, null);
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult && (mSelectedPackage != null)) {
if (callChangeListener(mSelectedPackage)) {
setValue(mSelectedPackage);
}
}
}
private int getIndexOfProviderList(String packageName) {
for (OpenPgpProviderEntry app : mList) {
if (app.packageName.equals(packageName)) {
return mList.indexOf(app);
}
}
return -1;
}
public void setValue(String packageName) {
mSelectedPackage = packageName;
persistString(packageName);
}
public String getValue() {
return mSelectedPackage;
}
public String getEntry() {
return getEntryByValue(mSelectedPackage);
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValue(restoreValue ? getPersistedString(mSelectedPackage) : (String) defaultValue);
}
public String getEntryByValue(String packageName) {
for (OpenPgpProviderEntry app : mList) {
if (app.packageName.equals(packageName)) {
return app.simpleName;
}
}
return null;
}
private static class OpenPgpProviderEntry {

View File

@ -0,0 +1,271 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openintents.openpgp.util;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.Preference;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.R;
public class OpenPgpKeyPreference extends Preference {
private long mKeyId;
private String mOpenPgpProvider;
private OpenPgpServiceConnection mServiceConnection;
private String mDefaultUserId;
public static final int REQUEST_CODE_KEY_PREFERENCE = 9999;
public OpenPgpKeyPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public CharSequence getSummary() {
return (mKeyId == 0) ? getContext().getString(R.string.openpgp_no_key_selected)
: getContext().getString(R.string.openpgp_key_selected);
}
private void updateEnabled() {
if (TextUtils.isEmpty(mOpenPgpProvider)) {
setEnabled(false);
} else {
setEnabled(true);
}
}
public void setOpenPgpProvider(String packageName) {
mOpenPgpProvider = packageName;
updateEnabled();
}
public void setDefaultUserId(String userId) {
mDefaultUserId = userId;
}
@Override
protected void onClick() {
// bind to service
mServiceConnection = new OpenPgpServiceConnection(
getContext().getApplicationContext(),
mOpenPgpProvider,
new OpenPgpServiceConnection.OnBound() {
@Override
public void onBound(IOpenPgpService service) {
Log.d(OpenPgpApi.TAG, "onBound!");
Intent data = new Intent();
data.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
data.putExtra(OpenPgpApi.EXTRA_USER_ID, mDefaultUserId);
OpenPgpApi api = new OpenPgpApi(getContext(), mServiceConnection.getService());
api.executeApiAsync(data, null, null, new MyCallback(REQUEST_CODE_KEY_PREFERENCE));
}
@Override
public void onError(Exception e) {
Log.e(OpenPgpApi.TAG, "exception when binding!", e);
}
}
);
mServiceConnection.bindToService();
}
private class MyCallback implements OpenPgpApi.IOpenPgpCallback {
int requestCode;
private MyCallback(int requestCode) {
this.requestCode = requestCode;
}
@Override
public void onReturn(Intent result) {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: {
Log.e(OpenPgpApi.TAG, "RESULT_CODE_SUCCESS: Should not happen!");
break;
}
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
try {
Activity act = (Activity) getContext();
act.startIntentSenderFromChild(
act, pi.getIntentSender(),
requestCode, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(OpenPgpApi.TAG, "SendIntentException", e);
}
break;
}
case OpenPgpApi.RESULT_CODE_ERROR: {
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
Log.e(OpenPgpApi.TAG, "RESULT_CODE_ERROR: " + error.getMessage());
break;
}
}
}
}
private void save(long newValue) {
// Give the client a chance to ignore this change if they deem it
// invalid
if (!callChangeListener(newValue)) {
// They don't want the value to be set
return;
}
setAndPersist(newValue);
}
private void setAndPersist(long newValue) {
mKeyId = newValue;
// Save to persistent storage (this method will make sure this
// preference should be persistent, along with other useful checks)
persistLong(mKeyId);
// Data has changed, notify so UI can be refreshed!
notifyChanged();
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
// This preference type's value type is Long, so we read the default
// value from the attributes as an Integer.
return (long) a.getInteger(index, 0);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
if (restoreValue) {
// Restore state
mKeyId = getPersistedLong(mKeyId);
} else {
// Set state
long value = (Long) defaultValue;
setAndPersist(value);
}
}
@Override
protected Parcelable onSaveInstanceState() {
/*
* Suppose a client uses this preference type without persisting. We
* must save the instance state so it is able to, for example, survive
* orientation changes.
*/
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
// No need to save instance state since it's persistent
return superState;
}
// Save the instance state
final SavedState myState = new SavedState(superState);
myState.keyId = mKeyId;
myState.openPgpProvider = mOpenPgpProvider;
myState.defaultUserId = mDefaultUserId;
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (!state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
// Restore the instance state
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
mKeyId = myState.keyId;
mOpenPgpProvider = myState.openPgpProvider;
mDefaultUserId = myState.defaultUserId;
notifyChanged();
}
/**
* SavedState, a subclass of {@link BaseSavedState}, will store the state
* of MyPreference, a subclass of Preference.
* <p/>
* It is important to always call through to super methods.
*/
private static class SavedState extends BaseSavedState {
long keyId;
String openPgpProvider;
String defaultUserId;
public SavedState(Parcel source) {
super(source);
keyId = source.readInt();
openPgpProvider = source.readString();
defaultUserId = source.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeLong(keyId);
dest.writeString(openPgpProvider);
dest.writeString(defaultUserId);
}
public SavedState(Parcelable superState) {
super(superState);
}
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_KEY_PREFERENCE && resultCode == Activity.RESULT_OK) {
long keyId = data.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, 0);
save(keyId);
return true;
} else {
return false;
}
}
}

View File

@ -71,7 +71,8 @@ public class OpenPgpUtils {
return hexString;
}
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: (\\[.*\\]))?(?: \\((.*)\\))?(?: <(.*)>)?$");
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
/**
* Splits userId string into naming part, email part, and comment part
@ -82,22 +83,42 @@ public class OpenPgpUtils {
* @param userId
* @return theParsedUserInfo
*/
public static UserInfo splitUserId(final String userId) {
public static UserId splitUserId(final String userId) {
if (!TextUtils.isEmpty(userId)) {
final Matcher matcher = USER_ID_PATTERN.matcher(userId);
if (matcher.matches()) {
return new UserInfo(matcher.group(1), matcher.group(4), matcher.group(3));
return new UserId(matcher.group(1), matcher.group(3), matcher.group(2));
}
}
return new UserInfo(null, null, null);
return new UserId(null, null, null);
}
public static class UserInfo {
/**
* Returns a composed user id. Returns null if name is null!
*
* @param name
* @param email
* @param comment
* @return
*/
public static String createUserId(UserId userId) {
String userIdString = userId.name; // consider name a required value
if (userIdString != null && !TextUtils.isEmpty(userId.comment)) {
userIdString += " (" + userId.comment + ")";
}
if (userIdString != null && !TextUtils.isEmpty(userId.email)) {
userIdString += " <" + userId.email + ">";
}
return userIdString;
}
public static class UserId {
public final String name;
public final String email;
public final String comment;
public UserInfo(String name, String email, String comment) {
public UserId(String name, String email, String comment) {
this.name = name;
this.email = email;
this.comment = comment;

View File

@ -3,5 +3,7 @@
<string name="openpgp_list_preference_none">None</string>
<string name="openpgp_install_openkeychain_via">Install OpenKeychain via %s</string>
<string name="openpgp_no_key_selected">No key selected</string>
<string name="openpgp_key_selected">Key has been selected</string>
</resources>