aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2014-08-06 01:08:12 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2014-08-06 01:08:12 +0200
commit881a50207af0a9f9f5aa69f451110de786779b54 (patch)
tree776bf4363c3e8736c3678eba93017387fffd8463 /OpenKeychain/src/main/java/org
parent0bfac9989f801aa93d8bc336307d60b817995688 (diff)
parent6ba7536838b8fbc69684bec7c3e847afcb5e9d6a (diff)
downloadopen-keychain-881a50207af0a9f9f5aa69f451110de786779b54.tar.gz
open-keychain-881a50207af0a9f9f5aa69f451110de786779b54.tar.bz2
open-keychain-881a50207af0a9f9f5aa69f451110de786779b54.zip
Merge branch 'master' into yubikey
Conflicts: .gitmodules OpenKeychain/build.gradle OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java extern/openpgp-api-lib settings.gradle
Diffstat (limited to 'OpenKeychain/src/main/java/org')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java44
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java64
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java201
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/TlsHelper.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java102
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java43
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java171
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java144
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java87
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java187
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java73
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java485
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java331
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java488
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java172
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java45
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java84
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java334
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java)134
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java187
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java83
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java187
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditUserIdDialogFragment.java62
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java88
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java271
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java380
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java62
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java428
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java267
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/DatabaseUtil.java36
85 files changed, 2839 insertions, 3041 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 16b6173f0..7d1af704d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -17,16 +17,16 @@
package org.sufficientlysecure.keychain;
-import android.os.Build;
import android.os.Environment;
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.remote.ui.AppsListActivity;
import org.sufficientlysecure.keychain.ui.DecryptActivity;
import org.sufficientlysecure.keychain.ui.EncryptActivity;
import org.sufficientlysecure.keychain.ui.KeyListActivity;
+import java.io.File;
+
public final class Constants {
public static final boolean DEBUG = BuildConfig.DEBUG;
@@ -49,12 +49,11 @@ public final class Constants {
public static final String CUSTOM_CONTACT_DATA_MIME_TYPE = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key";
- public static boolean KITKAT = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+ public static int TEMPFILE_TTL = 24 * 60 * 60 * 1000; // 1 day
public static final class Path {
- public static final String APP_DIR = Environment.getExternalStorageDirectory()
- + "/OpenKeychain";
- public static final String APP_DIR_FILE = APP_DIR + "/export.asc";
+ public static final File APP_DIR = new File(Environment.getExternalStorageDirectory(), "OpenKeychain");
+ public static final File APP_DIR_FILE = new File(APP_DIR, "export.asc");
}
public static final class Pref {
@@ -89,23 +88,6 @@ public final class Constants {
};
}
- public static final class choice {
- public static final class algorithm {
- // TODO: legacy reasons :/ better: PublicKeyAlgorithmTags
- public static final int dsa = 0x21070001;
- public static final int elgamal = 0x21070002;
- public static final int rsa = 0x21070003;
- }
-
- public static final class compression {
- // TODO: legacy reasons :/ better: CompressionAlgorithmTags.UNCOMPRESSED
- public static final int none = 0x21070001;
- public static final int zlib = CompressionAlgorithmTags.ZLIB;
- public static final int bzip2 = CompressionAlgorithmTags.BZIP2;
- public static final int zip = CompressionAlgorithmTags.ZIP;
- }
- }
-
public static final class key {
public static final int none = 0;
public static final int symmetric = -1;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index e70b134aa..9b9880533 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -28,11 +28,10 @@ import android.os.Environment;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.helper.TlsHelper;
+import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PRNGFixes;
-import java.io.File;
-import java.security.Provider;
import java.security.Security;
public class KeychainApplication extends Application {
@@ -73,8 +72,7 @@ public class KeychainApplication extends Application {
// Create APG directory on sdcard if not existing
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- File dir = new File(Constants.Path.APP_DIR);
- if (!dir.exists() && !dir.mkdirs()) {
+ if (!Constants.Path.APP_DIR.exists() && !Constants.Path.APP_DIR.mkdirs()) {
// ignore this for now, it's not crucial
// that the directory doesn't exist at this point
}
@@ -86,9 +84,11 @@ public class KeychainApplication extends Application {
setupAccountAsNeeded(this);
// Update keyserver list as needed
- Preferences.getPreferences(this).updateKeyServers();
+ Preferences.getPreferences(this).updatePreferences();
TlsHelper.addStaticCA("pool.sks-keyservers.net", getAssets(), "sks-keyservers.netCA.cer");
+
+ TemporaryStorageProvider.cleanUp(this);
}
public static void setupAccountAsNeeded(Context context) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java
index e639824ec..8697e49f7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java
@@ -19,8 +19,13 @@ package org.sufficientlysecure.keychain.helper;
import android.accounts.Account;
import android.accounts.AccountManager;
-import android.content.*;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
@@ -33,7 +38,14 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.util.Log;
-import java.util.*;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
public class ContactHelper {
@@ -60,6 +72,8 @@ public class ContactHelper {
ContactsContract.Data.RAW_CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?";
public static final String ID_SELECTION = ContactsContract.RawContacts._ID + "=?";
+ private static final Map<String, Bitmap> photoCache = new HashMap<String, Bitmap>();
+
public static List<String> getPossibleUserEmails(Context context) {
Set<String> accountMails = getAccountEmails(context);
accountMails.addAll(getMainProfileContactEmails(context));
@@ -232,6 +246,30 @@ public class ContactHelper {
return null;
}
+ public static Bitmap photoFromFingerprint(ContentResolver contentResolver, String fingerprint) {
+ if (fingerprint == null) return null;
+ if (!photoCache.containsKey(fingerprint)) {
+ photoCache.put(fingerprint, loadPhotoFromFingerprint(contentResolver, fingerprint));
+ }
+ return photoCache.get(fingerprint);
+ }
+
+ private static Bitmap loadPhotoFromFingerprint(ContentResolver contentResolver, String fingerprint) {
+ if (fingerprint == null) return null;
+ try {
+ int rawContactId = findRawContactId(contentResolver, fingerprint);
+ if (rawContactId == -1) return null;
+ Uri rawContactUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);
+ Uri contactUri = ContactsContract.RawContacts.getContactLookupUri(contentResolver, rawContactUri);
+ InputStream photoInputStream =
+ ContactsContract.Contacts.openContactPhotoInputStream(contentResolver, contactUri);
+ if (photoInputStream == null) return null;
+ return BitmapFactory.decodeStream(photoInputStream);
+ } catch (Throwable ignored) {
+ return null;
+ }
+ }
+
/**
* Write the current Keychain to the contact db
*/
@@ -356,7 +394,7 @@ public class ContactHelper {
int rawContactId, long masterKeyId) {
ops.add(selectByRawContactAndItemType(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI),
rawContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE).build());
- Cursor ids = resolver.query(KeychainContract.UserIds.buildUserIdsUri(Long.toString(masterKeyId)),
+ Cursor ids = resolver.query(KeychainContract.UserIds.buildUserIdsUri(masterKeyId),
USER_IDS_PROJECTION, NON_REVOKED_SELECTION, null, null);
if (ids != null) {
while (ids.moveToNext()) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java
index 5d281d5b0..d8efdc480 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/EmailKeyHelper.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Messenger;
+
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
@@ -29,6 +30,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Set;
public class EmailKeyHelper {
@@ -86,7 +88,7 @@ public class EmailKeyHelper {
for (ImportKeysListEntry key : keyServer.search(mail)) {
if (key.isRevoked() || key.isExpired()) continue;
for (String userId : key.getUserIds()) {
- if (userId.toLowerCase().contains(mail.toLowerCase())) {
+ if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) {
keys.add(key);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
index 16ef28311..bcd57b290 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
@@ -30,18 +30,17 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import java.io.File;
+
public class ExportHelper {
- protected FileDialogFragment mFileDialog;
- protected String mExportFilename;
+ protected File mExportFile;
ActionBarActivity mActivity;
@@ -68,47 +67,30 @@ public class ExportHelper {
/**
* Show dialog where to export keys
*/
- public void showExportKeysDialog(final long[] masterKeyIds, final String exportFilename,
+ public void showExportKeysDialog(final long[] masterKeyIds, final File exportFile,
final boolean showSecretCheckbox) {
- mExportFilename = exportFilename;
-
- // Message is received after file is selected
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == FileDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
-
- exportKeys(masterKeyIds, data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED));
- }
- }
- };
+ mExportFile = exportFile;
- // Create a new Messenger for the communication back
- final Messenger messenger = new Messenger(returnHandler);
-
- DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
- public void run() {
- String title = null;
- if (masterKeyIds == null) {
- // export all keys
- title = mActivity.getString(R.string.title_export_keys);
- } else {
- // export only key specified at data uri
- title = mActivity.getString(R.string.title_export_key);
- }
-
- String message = mActivity.getString(R.string.specify_file_to_export_to);
- String checkMsg = showSecretCheckbox ?
- mActivity.getString(R.string.also_export_secret_keys) : null;
+ String title = null;
+ if (masterKeyIds == null) {
+ // export all keys
+ title = mActivity.getString(R.string.title_export_keys);
+ } else {
+ // export only key specified at data uri
+ title = mActivity.getString(R.string.title_export_key);
+ }
- mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
- exportFilename, checkMsg);
+ String message = mActivity.getString(R.string.specify_file_to_export_to);
+ String checkMsg = showSecretCheckbox ?
+ mActivity.getString(R.string.also_export_secret_keys) : null;
- mFileDialog.show(mActivity.getSupportFragmentManager(), "fileDialog");
+ FileHelper.saveFile(new FileHelper.FileDialogCallback() {
+ @Override
+ public void onFileSelected(File file, boolean checked) {
+ mExportFile = file;
+ exportKeys(masterKeyIds, checked);
}
- });
+ }, mActivity.getSupportFragmentManager() ,title, message, exportFile, checkMsg);
}
/**
@@ -125,7 +107,7 @@ public class ExportHelper {
// fill values for this action
Bundle data = new Bundle();
- data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
+ data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFile.getAbsolutePath());
data.putBoolean(KeychainIntentService.EXPORT_SECRET, exportSecret);
if (masterKeyIds == null) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
index e0c94b947..b640ecb03 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
@@ -23,15 +23,26 @@ import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.provider.DocumentsContract;
+import android.provider.OpenableColumns;
import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
import android.widget.Toast;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
+
+import java.io.File;
+import java.text.DecimalFormat;
public class FileHelper {
@@ -55,25 +66,18 @@ public class FileHelper {
* Opens the preferred installed file manager on Android and shows a toast if no manager is
* installed.
*
- * @param activity
- * @param filename default selected file, not supported by all file managers
+ * @param fragment
+ * @param last default selected Uri, not supported by all file managers
* @param mimeType can be text/plain for example
* @param requestCode requestCode used to identify the result coming back from file manager to
* onActivityResult() in your activity
*/
- public static void openFile(Activity activity, String filename, String mimeType, int requestCode) {
- Intent intent = buildFileIntent(filename, mimeType);
-
- try {
- activity.startActivityForResult(intent, requestCode);
- } catch (ActivityNotFoundException e) {
- // No compatible file manager was found.
- Toast.makeText(activity, R.string.no_filemanager_installed, Toast.LENGTH_SHORT).show();
- }
- }
+ public static void openFile(Fragment fragment, Uri last, String mimeType, int requestCode) {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
- public static void openFile(Fragment fragment, String filename, String mimeType, int requestCode) {
- Intent intent = buildFileIntent(filename, mimeType);
+ intent.setData(last);
+ intent.setType(mimeType);
try {
fragment.startActivityForResult(intent, requestCode);
@@ -84,86 +88,153 @@ public class FileHelper {
}
}
+ public static void saveFile(final FileDialogCallback callback, final FragmentManager fragmentManager,
+ final String title, final String message, final File defaultFile,
+ final String checkMsg) {
+ // Message is received after file is selected
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == FileDialogFragment.MESSAGE_OKAY) {
+ callback.onFileSelected(
+ new File(message.getData().getString(FileDialogFragment.MESSAGE_DATA_FILE)),
+ message.getData().getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED));
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ final Messenger messenger = new Messenger(returnHandler);
+
+ DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
+ @Override
+ public void run() {
+ FileDialogFragment fileDialog = FileDialogFragment.newInstance(messenger, title, message,
+ defaultFile, checkMsg);
+
+ fileDialog.show(fragmentManager, "fileDialog");
+ }
+ });
+ }
+
+ public static void saveFile(Fragment fragment, String title, String message, File defaultFile, int requestCode) {
+ saveFile(fragment, title, message, defaultFile, requestCode, null);
+ }
+
+ public static void saveFile(final Fragment fragment, String title, String message, File defaultFile,
+ final int requestCode, String checkMsg) {
+ saveFile(new FileDialogCallback() {
+ @Override
+ public void onFileSelected(File file, boolean checked) {
+ Intent intent = new Intent();
+ intent.setData(Uri.fromFile(file));
+ fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent);
+ }
+ }, fragment.getActivity().getSupportFragmentManager(), title, message, defaultFile, checkMsg);
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ public static void openDocument(Fragment fragment, String mimeType, int requestCode) {
+ openDocument(fragment, mimeType, false, requestCode);
+ }
/**
* Opens the storage browser on Android 4.4 or later for opening a file
+ *
* @param fragment
- * @param last default selected file
- * @param mimeType can be text/plain for example
+ * @param mimeType can be text/plain for example
+ * @param multiple allow file chooser to return multiple files
* @param requestCode used to identify the result coming back from storage browser onActivityResult() in your
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
- public static void openDocument(Fragment fragment, Uri last, String mimeType, int requestCode) {
+ public static void openDocument(Fragment fragment, String mimeType, boolean multiple, int requestCode) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setData(last);
intent.setType(mimeType);
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple);
+
fragment.startActivityForResult(intent, requestCode);
}
/**
* Opens the storage browser on Android 4.4 or later for saving a file
+ *
* @param fragment
- * @param last default selected file
- * @param mimeType can be text/plain for example
- * @param requestCode used to identify the result coming back from storage browser onActivityResult() in your
+ * @param mimeType can be text/plain for example
+ * @param suggestedName a filename desirable for the file to be saved
+ * @param requestCode used to identify the result coming back from storage browser onActivityResult() in your
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
- public static void saveDocument(Fragment fragment, Uri last, String mimeType, int requestCode) {
+ public static void saveDocument(Fragment fragment, String mimeType, String suggestedName, int requestCode) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setData(last);
intent.setType(mimeType);
+ intent.putExtra("android.content.extra.SHOW_ADVANCED", true); // Note: This is not documented, but works
+ intent.putExtra(Intent.EXTRA_TITLE, suggestedName);
fragment.startActivityForResult(intent, requestCode);
}
- private static Intent buildFileIntent(String filename, String mimeType) {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
+ public static String getFilename(Context context, Uri uri) {
+ String filename = null;
+ try {
+ Cursor cursor = context.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
- intent.setData(Uri.parse("file://" + filename));
- intent.setType(mimeType);
+ if (cursor != null) {
+ if (cursor.moveToNext()) {
+ filename = cursor.getString(0);
+ }
+ cursor.close();
+ }
+ } catch (Exception ignored) {
+ // This happens in rare cases (eg: document deleted since selection) and should not cause a failure
+ }
+ if (filename == null) {
+ String[] split = uri.toString().split("/");
+ filename = split[split.length - 1];
+ }
+ return filename;
+ }
- return intent;
+ public static long getFileSize(Context context, Uri uri) {
+ return getFileSize(context, uri, -1);
}
- /**
- * Get a file path from a Uri.
- * <p/>
- * from https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/
- * afilechooser/utils/FileUtils.java
- *
- * @param context
- * @param uri
- * @return
- * @author paulburke
- */
- public static String getPath(Context context, Uri uri) {
- Log.d(Constants.TAG + " File -",
- "Authority: " + uri.getAuthority() + ", Fragment: " + uri.getFragment()
- + ", Port: " + uri.getPort() + ", Query: " + uri.getQuery() + ", Scheme: "
- + uri.getScheme() + ", Host: " + uri.getHost() + ", Segments: "
- + uri.getPathSegments().toString());
-
- if ("content".equalsIgnoreCase(uri.getScheme())) {
- String[] projection = {"_data"};
- Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int columnIndex = cursor.getColumnIndexOrThrow("_data");
- return cursor.getString(columnIndex);
- }
- } catch (Exception e) {
- // Eat it
- } finally {
- if (cursor != null) {
- cursor.close();
+ public static long getFileSize(Context context, Uri uri, long def) {
+ long size = def;
+ try {
+ Cursor cursor = context.getContentResolver().query(uri, new String[]{OpenableColumns.SIZE}, null, null, null);
+
+ if (cursor != null) {
+ if (cursor.moveToNext()) {
+ size = cursor.getLong(0);
}
+ cursor.close();
}
- } else if ("file".equalsIgnoreCase(uri.getScheme())) {
- return uri.getPath();
+ } catch (Exception ignored) {
+ // This happens in rare cases (eg: document deleted since selection) and should not cause a failure
+ }
+ return size;
+ }
+
+ /**
+ * Retrieve thumbnail of file, document api feature and thus KitKat only
+ */
+ public static Bitmap getThumbnail(Context context, Uri uri, Point size) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ return DocumentsContract.getDocumentThumbnail(context.getContentResolver(), uri, size, null);
+ } else {
+ return null;
}
+ }
+
+ public static String readableFileSize(long size) {
+ if (size <= 0) return "0";
+ final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
+ int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
+ return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
+ }
- return null;
+ public static interface FileDialogCallback {
+ public void onFileSelected(File file, boolean checked);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
index a060092a3..491709354 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.helper;
import android.content.Context;
import android.content.SharedPreferences;
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
@@ -99,7 +100,7 @@ public class Preferences {
public int getDefaultMessageCompression() {
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION,
- Constants.choice.compression.zlib);
+ CompressionAlgorithmTags.ZLIB);
}
public void setDefaultMessageCompression(int value) {
@@ -110,7 +111,7 @@ public class Preferences {
public int getDefaultFileCompression() {
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION,
- Constants.choice.compression.none);
+ CompressionAlgorithmTags.UNCOMPRESSED);
}
public void setDefaultFileCompression(int value) {
@@ -170,7 +171,8 @@ public class Preferences {
editor.commit();
}
- public void updateKeyServers() {
+ public void updatePreferences() {
+ // migrate keyserver to hkps
if (mSharedPreferences.getInt(Constants.Pref.KEY_SERVERS_DEFAULT_VERSION, 0) !=
Constants.Defaults.KEY_SERVERS_VERSION) {
String[] servers = getKeyServers();
@@ -186,6 +188,11 @@ public class Preferences {
.putInt(Constants.Pref.KEY_SERVERS_DEFAULT_VERSION, Constants.Defaults.KEY_SERVERS_VERSION)
.commit();
}
+
+ // migrate old uncompressed constant to new one
+ if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, 0) == 0x21070001) {
+ setDefaultFileCompression(CompressionAlgorithmTags.UNCOMPRESSED);
+ }
}
public void setConcealPgpApplication(boolean conceal) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/TlsHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/TlsHelper.java
index 4b09af04d..8bbe73676 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/TlsHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/TlsHelper.java
@@ -18,12 +18,10 @@
package org.sufficientlysecure.keychain.helper;
import android.content.res.AssetManager;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManagerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -34,10 +32,16 @@ import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
-import java.security.cert.*;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
import java.util.HashMap;
import java.util.Map;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+
public class TlsHelper {
public static class TlsHelperException extends Exception {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
index 41f1e6997..f617be62a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
@@ -18,11 +18,6 @@
package org.sufficientlysecure.keychain.keyimport;
-import de.measite.minidns.Client;
-import de.measite.minidns.Question;
-import de.measite.minidns.Record;
-import de.measite.minidns.record.SRV;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.helper.TlsHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
@@ -45,6 +40,11 @@ import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import de.measite.minidns.Client;
+import de.measite.minidns.Question;
+import de.measite.minidns.Record;
+import de.measite.minidns.record.SRV;
+
public class HkpKeyserver extends Keyserver {
private static class HttpError extends Exception {
private static final long serialVersionUID = 1718783705229428893L;
@@ -251,14 +251,14 @@ public class HkpKeyserver extends Keyserver {
data = query(request);
} catch (HttpError e) {
if (e.getData() != null) {
- Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.US));
+ Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.ENGLISH));
- if (e.getData().toLowerCase(Locale.US).contains("no keys found")) {
+ if (e.getData().toLowerCase(Locale.ENGLISH).contains("no keys found")) {
// NOTE: This is also a 404 error for some keyservers!
return results;
- } else if (e.getData().toLowerCase(Locale.US).contains("too many")) {
+ } else if (e.getData().toLowerCase(Locale.ENGLISH).contains("too many")) {
throw new TooManyResponsesException();
- } else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) {
+ } else if (e.getData().toLowerCase(Locale.ENGLISH).contains("insufficient")) {
throw new QueryTooShortException();
} else if (e.getCode() == 404) {
// NOTE: handle this 404 at last, maybe it was a "no keys found" error
@@ -285,7 +285,7 @@ public class HkpKeyserver extends Keyserver {
// group 1 contains the full fingerprint (v4) or the long key id if available
// see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr
- String fingerprintOrKeyId = matcher.group(1).toLowerCase(Locale.US);
+ String fingerprintOrKeyId = matcher.group(1).toLowerCase(Locale.ENGLISH);
if (fingerprintOrKeyId.length() > 16) {
entry.setFingerprintHex(fingerprintOrKeyId);
entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
index 066c51a13..8609a7082 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
@@ -1,3 +1,20 @@
+/*
+ * 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.keyimport;
import android.os.Parcel;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
index ee0dfefa4..1da66872d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
@@ -1,3 +1,20 @@
+/*
+ * 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 org.spongycastle.openpgp.PGPKeyRing;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
index 981caad49..ce6498df1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
@@ -1,3 +1,20 @@
+/*
+ * 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 org.spongycastle.openpgp.PGPPublicKey;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
index 70288dceb..972e45c2e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
@@ -1,3 +1,20 @@
+/*
+ * 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 org.spongycastle.bcpg.ArmoredOutputStream;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index ff82da07a..e39924f7e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -1,3 +1,20 @@
+/*
+ * 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 org.spongycastle.bcpg.HashAlgorithmTags;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
index 7d3e26000..4f74a2336 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
@@ -1,3 +1,20 @@
+/*
+ * 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 org.spongycastle.bcpg.S2K;
@@ -6,7 +23,6 @@ import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
index 7d11a20d3..35020b815 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -1,3 +1,20 @@
+/*
+ * 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.text.TextUtils;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java
index 68312dca3..5ef4f7998 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java
@@ -1,3 +1,20 @@
+/*
+ * 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;
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
deleted file mode 100644
index 3a5a96fbb..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2012-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 org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-
-public class PgpConversionHelper {
-
- /**
- * Convert from byte[] to ArrayList<PGPSecretKey>
- *
- * @param keysBytes
- * @return
- */
- public static ArrayList<UncachedSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
- PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
- Object obj = null;
- ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
- try {
- while ((obj = factory.nextObject()) != null) {
- PGPSecretKey secKey = null;
- if (obj instanceof PGPSecretKey) {
- secKey = (PGPSecretKey) obj;
- if (secKey == null) {
- Log.e(Constants.TAG, "No keys given!");
- }
- keys.add(new UncachedSecretKey(secKey));
- } else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
- PGPSecretKeyRing keyRing = null;
- keyRing = (PGPSecretKeyRing) obj;
- if (keyRing == null) {
- Log.e(Constants.TAG, "No keys given!");
- }
- @SuppressWarnings("unchecked")
- Iterator<PGPSecretKey> itr = keyRing.getSecretKeys();
- while (itr.hasNext()) {
- keys.add(new UncachedSecretKey(itr.next()));
- }
- }
- }
- } catch (IOException e) {
- }
-
- return keys;
- }
-
- /**
- * Convert from byte[] to PGPSecretKey
- * <p/>
- * Singles keys are encoded as keyRings with one single key in it by Bouncy Castle
- *
- * @param keyBytes
- * @return
- */
- public static UncachedSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
- PGPObjectFactory factory = new PGPObjectFactory(keyBytes);
- Object obj = null;
- try {
- obj = factory.nextObject();
- } catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting to PGPSecretKey!", e);
- }
- PGPSecretKey secKey = null;
- if (obj instanceof PGPSecretKey) {
- if ((secKey = (PGPSecretKey) obj) == null) {
- Log.e(Constants.TAG, "No keys given!");
- }
- } else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
- PGPSecretKeyRing keyRing = null;
- if ((keyRing = (PGPSecretKeyRing) obj) == null) {
- Log.e(Constants.TAG, "No keys given!");
- }
- secKey = keyRing.getSecretKey();
- }
-
- return new UncachedSecretKey(secKey);
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index 20bb1f97c..459b80be2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -24,7 +24,7 @@ import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
-import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -37,9 +37,6 @@ import java.util.Locale;
public class PgpKeyHelper {
- /**
- * TODO: Only used in HkpKeyServer. Get rid of this one!
- */
public static String getAlgorithmInfo(int algorithm) {
return getAlgorithmInfo(null, algorithm, 0);
}
@@ -55,25 +52,25 @@ public class PgpKeyHelper {
String algorithmStr;
switch (algorithm) {
- case PGPPublicKey.RSA_ENCRYPT:
- case PGPPublicKey.RSA_GENERAL:
- case PGPPublicKey.RSA_SIGN: {
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ case PublicKeyAlgorithmTags.RSA_SIGN: {
algorithmStr = "RSA";
break;
}
- case PGPPublicKey.DSA: {
+ case PublicKeyAlgorithmTags.DSA: {
algorithmStr = "DSA";
break;
}
- case PGPPublicKey.ELGAMAL_ENCRYPT:
- case PGPPublicKey.ELGAMAL_GENERAL: {
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
+ case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: {
algorithmStr = "ElGamal";
break;
}
- case PGPPublicKey.ECDSA:
- case PGPPublicKey.ECDH: {
+ case PublicKeyAlgorithmTags.ECDSA:
+ case PublicKeyAlgorithmTags.ECDH: {
algorithmStr = "ECC";
break;
}
@@ -82,7 +79,6 @@ public class PgpKeyHelper {
if (context != null) {
algorithmStr = context.getResources().getString(R.string.unknown_algorithm);
} else {
- // TODO
algorithmStr = "unknown";
}
break;
@@ -104,7 +100,7 @@ public class PgpKeyHelper {
* @return
*/
public static String convertFingerprintToHex(byte[] fingerprint) {
- String hexString = Hex.toHexString(fingerprint).toLowerCase(Locale.US);
+ String hexString = Hex.toHexString(fingerprint).toLowerCase(Locale.ENGLISH);
return hexString;
}
@@ -133,7 +129,7 @@ public class PgpKeyHelper {
}
private static String convertKeyIdToHex32bit(long keyId) {
- String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.US);
+ String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.ENGLISH);
while (hexString.length() < 8) {
hexString = "0" + hexString;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 19b0d81b7..bb692555e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.spec.ElGamalParameterSpec;
@@ -138,7 +139,7 @@ public class PgpKeyOperation {
KeyPairGenerator keyGen;
switch (algorithmChoice) {
- case Constants.choice.algorithm.dsa: {
+ case PublicKeyAlgorithmTags.DSA: {
progress(R.string.progress_generating_dsa, 30);
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom());
@@ -146,7 +147,7 @@ public class PgpKeyOperation {
break;
}
- case Constants.choice.algorithm.elgamal: {
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: {
progress(R.string.progress_generating_elgamal, 30);
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(keySize);
@@ -159,7 +160,7 @@ public class PgpKeyOperation {
break;
}
- case Constants.choice.algorithm.rsa: {
+ case PublicKeyAlgorithmTags.RSA_GENERAL: {
progress(R.string.progress_generating_rsa, 30);
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom());
@@ -217,7 +218,7 @@ public class PgpKeyOperation {
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- if (add.mAlgorithm == Constants.choice.algorithm.elgamal) {
+ if (add.mAlgorithm == PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
@@ -356,7 +357,7 @@ public class PgpKeyOperation {
progress(R.string.progress_modify_adduid, (i-1) * (100 / saveParcel.mAddUserIds.size()));
String userId = saveParcel.mAddUserIds.get(i);
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent);
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent, userId);
if (userId.equals("")) {
log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1);
@@ -768,7 +769,7 @@ public class PgpKeyOperation {
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
// If this key can sign, we need a primary key binding signature
- if ((flags & KeyFlags.SIGN_DATA) != 0) {
+ if ((flags & KeyFlags.SIGN_DATA) > 0) {
// cross-certify signing keys
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 09c28e7c5..67eced699 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream;
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.openpgp.PGPCompressedDataGenerator;
import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException;
@@ -116,7 +117,7 @@ public class PgpSignEncrypt {
// optional
private Progressable mProgressable = null;
private boolean mEnableAsciiArmorOutput = false;
- private int mCompressionId = Constants.choice.compression.none;
+ private int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
private long[] mEncryptionMasterKeyIds = null;
private String mSymmetricPassphrase = null;
private int mSymmetricEncryptionAlgorithm = 0;
@@ -264,7 +265,7 @@ public class PgpSignEncrypt {
boolean enableSignature = mSignatureMasterKeyId != Constants.key.none;
boolean enableEncryption = ((mEncryptionMasterKeyIds != null && mEncryptionMasterKeyIds.length > 0)
|| mSymmetricPassphrase != null);
- boolean enableCompression = (mCompressionId != Constants.choice.compression.none);
+ boolean enableCompression = (mCompressionId != CompressionAlgorithmTags.UNCOMPRESSED);
Log.d(Constants.TAG, "enableSignature:" + enableSignature
+ "\nenableEncryption:" + enableEncryption
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index d29f19d67..18ddaa0ec 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -1,3 +1,20 @@
+/*
+ * 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 org.spongycastle.bcpg.ArmoredOutputStream;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
index 4a03d942b..341ca6d04 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -1,3 +1,20 @@
+/*
+ * 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 org.spongycastle.bcpg.sig.KeyFlags;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
index 0e14a7fd3..8dc28c2b3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
@@ -1,3 +1,20 @@
+/*
+ * 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 org.spongycastle.bcpg.sig.KeyFlags;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
index 07fb4fb9e..ebd110dc5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -1,3 +1,20 @@
+/*
+ * 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 org.spongycastle.bcpg.SignatureSubpacket;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
index aa0207a6a..e076fd9cc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
@@ -1,3 +1,20 @@
+/*
+ * 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.provider;
import android.database.Cursor;
@@ -201,7 +218,7 @@ public class CachedPublicKeyRing extends KeyRing {
}
private Cursor getSubkeys() throws PgpGeneralException {
- Uri keysUri = KeychainContract.Keys.buildKeysUri(Long.toString(extractOrGetMasterKeyId()));
+ Uri keysUri = KeychainContract.Keys.buildKeysUri(extractOrGetMasterKeyId());
return mProviderHelper.getContentResolver().query(keysUri, null, null, null, null);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 483f762f7..56168847f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -172,8 +172,8 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
}
- public static Uri buildPublicKeyRingUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_PUBLIC).build();
+ public static Uri buildPublicKeyRingUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_PUBLIC).build();
}
public static Uri buildPublicKeyRingUri(Uri uri) {
@@ -184,8 +184,8 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build();
}
- public static Uri buildSecretKeyRingUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_SECRET).build();
+ public static Uri buildSecretKeyRingUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_SECRET).build();
}
public static Uri buildSecretKeyRingUri(Uri uri) {
@@ -210,8 +210,8 @@ public class KeychainContract {
public static final String CONTENT_ITEM_TYPE
= "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.keychain.keys";
- public static Uri buildKeysUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_KEYS).build();
+ public static Uri buildKeysUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_KEYS).build();
}
public static Uri buildKeysUri(Uri uri) {
@@ -237,8 +237,8 @@ public class KeychainContract {
public static final String CONTENT_ITEM_TYPE
= "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.user_ids";
- public static Uri buildUserIdsUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_USER_IDS).build();
+ public static Uri buildUserIdsUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_USER_IDS).build();
}
public static Uri buildUserIdsUri(Uri uri) {
@@ -304,12 +304,14 @@ public class KeychainContract {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
- public static Uri buildCertsUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_CERTS).build();
+ public static Uri buildCertsUri(long masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_CERTS).build();
}
- public static Uri buildCertsSpecificUri(String masterKeyId, String rank, String certifier) {
- return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_CERTS).appendPath(rank).appendPath(certifier).build();
+ public static Uri buildCertsSpecificUri(long masterKeyId, long rank, long certifier) {
+ return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId))
+ .appendPath(PATH_CERTS).appendPath(Long.toString(rank))
+ .appendPath(Long.toString(certifier)).build();
}
public static Uri buildCertsUri(Uri uri) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 7a63ec3d7..3a859f505 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -165,8 +165,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
// make sure this is only done once, on the first instance!
boolean iAmIt = false;
- synchronized(KeychainDatabase.class) {
- if(!KeychainDatabase.apgHack) {
+ synchronized (KeychainDatabase.class) {
+ if (!KeychainDatabase.apgHack) {
iAmIt = true;
KeychainDatabase.apgHack = true;
}
@@ -316,42 +316,37 @@ public class KeychainDatabase extends SQLiteOpenHelper {
}
private static void copy(File in, File out) throws IOException {
- FileInputStream ss = new FileInputStream(in);
- FileOutputStream ds = new FileOutputStream(out);
+ FileInputStream is = new FileInputStream(in);
+ FileOutputStream os = new FileOutputStream(out);
byte[] buf = new byte[512];
- while (ss.available() > 0) {
- int count = ss.read(buf, 0, 512);
- ds.write(buf, 0, count);
+ while (is.available() > 0) {
+ int count = is.read(buf, 0, 512);
+ os.write(buf, 0, count);
}
}
- public static void debugRead(Context context) throws IOException {
+ public static void debugBackup(Context context, boolean restore) throws IOException {
if (!Constants.DEBUG) {
return;
}
- File in = context.getDatabasePath("debug.db");
- File out = context.getDatabasePath("openkeychain.db");
- if (!in.canRead()) {
- throw new IOException("Cannot read " + in.getName());
- }
- if (!out.canRead()) {
- throw new IOException("Cannot write " + out.getName());
- }
- copy(in, out);
- }
- public static void debugWrite(Context context) throws IOException {
- if (!Constants.DEBUG) {
- return;
+ File in;
+ File out;
+ if (restore) {
+ in = context.getDatabasePath("debug_backup.db");
+ out = context.getDatabasePath(DATABASE_NAME);
+ } else {
+ in = context.getDatabasePath(DATABASE_NAME);
+ out = context.getDatabasePath("debug_backup.db");
+ out.createNewFile();
}
- File in = context.getDatabasePath("openkeychain.db");
- File out = context.getDatabasePath("debug.db");
if (!in.canRead()) {
throw new IOException("Cannot read " + in.getName());
}
- if (!out.canRead()) {
+ if (!out.canWrite()) {
throw new IOException("Cannot write " + out.getName());
}
copy(in, out);
}
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index aa85577e0..6111a4ef4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -28,17 +28,14 @@ import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.NullProgressable;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
-import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
-import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
-import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
@@ -51,6 +48,9 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.remote.AppSettings;
+import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
+import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
+import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -299,7 +299,7 @@ public class ProviderHelper {
return SaveKeyringResult.RESULT_ERROR;
}
- Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
+ Uri uri = KeyRingData.buildPublicKeyRingUri(masterKeyId);
operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build());
}
@@ -307,7 +307,7 @@ public class ProviderHelper {
progress.setProgress(LogType.MSG_IP_INSERT_SUBKEYS.getMsgId(), 40, 100);
mIndent += 1;
{ // insert subkeys
- Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
+ Uri uri = Keys.buildKeysUri(masterKeyId);
int rank = 0;
for (CanonicalizedPublicKey key : keyRing.publicKeyIterator()) {
long keyId = key.getKeyId();
@@ -498,7 +498,7 @@ public class ProviderHelper {
try {
// delete old version of this keyRing, which also deletes all keys and userIds on cascade
int deleted = mContentResolver.delete(
- KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null);
+ KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null);
if (deleted > 0) {
log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK);
result |= SaveKeyringResult.UPDATED;
@@ -567,7 +567,7 @@ public class ProviderHelper {
values.put(KeyRingData.MASTER_KEY_ID, masterKeyId);
values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded());
// insert new version of this keyRing
- Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
+ Uri uri = KeyRingData.buildSecretKeyRingUri(masterKeyId);
if (mContentResolver.insert(uri, values) == null) {
log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION);
return SaveKeyringResult.RESULT_ERROR;
@@ -579,7 +579,7 @@ public class ProviderHelper {
}
{
- Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
+ Uri uri = Keys.buildKeysUri(masterKeyId);
// first, mark all keys as not available
ContentValues values = new ContentValues();
@@ -836,7 +836,7 @@ public class ProviderHelper {
values.put(Certs.VERIFIED, verified);
values.put(Certs.DATA, cert.getEncoded());
- Uri uri = Certs.buildCertsUri(Long.toString(masterKeyId));
+ Uri uri = Certs.buildCertsUri(masterKeyId);
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
@@ -853,7 +853,7 @@ public class ProviderHelper {
values.put(UserIds.IS_REVOKED, item.isRevoked);
values.put(UserIds.RANK, rank);
- Uri uri = UserIds.buildUserIdsUri(Long.toString(masterKeyId));
+ Uri uri = UserIds.buildUserIdsUri(masterKeyId);
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
new file mode 100644
index 000000000..87c0cc0a6
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
@@ -0,0 +1,171 @@
+/*
+ * 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.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.OpenableColumns;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.DatabaseUtil;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+public class TemporaryStorageProvider extends ContentProvider {
+
+ private static final String DB_NAME = "tempstorage.db";
+ private static final String TABLE_FILES = "files";
+ private static final String COLUMN_ID = "id";
+ private static final String COLUMN_NAME = "name";
+ private static final String COLUMN_TIME = "time";
+ private static final Uri BASE_URI = Uri.parse("content://org.sufficientlysecure.keychain.tempstorage/");
+ private static final int DB_VERSION = 1;
+
+ public static Uri createFile(Context context, String targetName) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(COLUMN_NAME, targetName);
+ return context.getContentResolver().insert(BASE_URI, contentValues);
+ }
+
+ public static int cleanUp(Context context) {
+ return context.getContentResolver().delete(BASE_URI, COLUMN_TIME + "< ?",
+ new String[]{Long.toString(System.currentTimeMillis() - Constants.TEMPFILE_TTL)});
+ }
+
+ private class TemporaryStorageDatabase extends SQLiteOpenHelper {
+
+ public TemporaryStorageDatabase(Context context) {
+ super(context, DB_NAME, null, DB_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_FILES + " (" +
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ COLUMN_NAME + " TEXT, " +
+ COLUMN_TIME + " INTEGER" +
+ ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+
+ }
+ }
+
+ private TemporaryStorageDatabase db;
+
+ private File getFile(Uri uri) throws FileNotFoundException {
+ try {
+ return getFile(Integer.parseInt(uri.getLastPathSegment()));
+ } catch (NumberFormatException e) {
+ throw new FileNotFoundException();
+ }
+ }
+
+ private File getFile(int id) {
+ return new File(getContext().getCacheDir(), "temp/" + id);
+ }
+
+ @Override
+ public boolean onCreate() {
+ db = new TemporaryStorageDatabase(getContext());
+ return new File(getContext().getCacheDir(), "temp").mkdirs();
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ File file;
+ try {
+ file = getFile(uri);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ Cursor fileName = db.getReadableDatabase().query(TABLE_FILES, new String[]{COLUMN_NAME}, COLUMN_ID + "=?",
+ new String[]{uri.getLastPathSegment()}, null, null, null);
+ if (fileName != null) {
+ if (fileName.moveToNext()) {
+ MatrixCursor cursor =
+ new MatrixCursor(new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE, "_data"});
+ cursor.newRow().add(fileName.getString(0)).add(file.length()).add(file.getAbsolutePath());
+ fileName.close();
+ return cursor;
+ }
+ fileName.close();
+ }
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ // Note: If we can find a files mime type, we can decrypt it to temp storage and open it after
+ // encryption. The mime type is needed, else UI really sucks and some apps break.
+ return "*/*";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ if (!values.containsKey(COLUMN_TIME)) {
+ values.put(COLUMN_TIME, System.currentTimeMillis());
+ }
+ int insert = (int) db.getWritableDatabase().insert(TABLE_FILES, null, values);
+ try {
+ getFile(insert).createNewFile();
+ } catch (IOException e) {
+ return null;
+ }
+ return Uri.withAppendedPath(BASE_URI, Long.toString(insert));
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ if (uri.getLastPathSegment() != null) {
+ selection = DatabaseUtil.concatenateWhere(selection, COLUMN_ID + "=?");
+ selectionArgs = DatabaseUtil.appendSelectionArgs(selectionArgs, new String[]{uri.getLastPathSegment()});
+ }
+ Cursor files = db.getReadableDatabase().query(TABLE_FILES, new String[]{COLUMN_ID}, selection,
+ selectionArgs, null, null, null);
+ if (files != null) {
+ while (files.moveToNext()) {
+ getFile(files.getInt(0)).delete();
+ }
+ files.close();
+ return db.getWritableDatabase().delete(TABLE_FILES, selection, selectionArgs);
+ }
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("Update not supported");
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ return openFileHelper(uri, mode);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java
index a25ecded6..d6013b49d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.remote;
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
@@ -39,7 +40,7 @@ public class AccountSettings {
// defaults:
this.mEncryptionAlgorithm = PGPEncryptedData.AES_256;
this.mHashAlgorithm = HashAlgorithmTags.SHA512;
- this.mCompression = Constants.choice.compression.zlib;
+ this.mCompression = CompressionAlgorithmTags.ZLIB;
}
public String getAccountName() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
index 2ba792f9a..8468f5eca 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -26,9 +26,9 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
-import android.widget.Button;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
index 6c4d59a77..43ed2dad0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
@@ -23,7 +23,13 @@ import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Intent;
import android.content.SyncResult;
-import android.os.*;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.KeychainApplication;
import org.sufficientlysecure.keychain.helper.ContactHelper;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java
index 008502ce7..41ff6d02b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java
@@ -29,6 +29,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.Toast;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index 7250a861d..a7115a53d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -24,7 +24,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
-import android.os.Parcel;
import android.os.RemoteException;
import org.sufficientlysecure.keychain.Constants;
@@ -32,7 +31,6 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.util.FileImportCache;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
@@ -56,6 +54,7 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
+import org.sufficientlysecure.keychain.util.FileImportCache;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -63,11 +62,9 @@ import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -110,8 +107,10 @@ public class KeychainIntentService extends IntentService
public static final String SOURCE = "source";
// possible targets:
public static final int IO_BYTES = 1;
- public static final int IO_FILE = 2; // This was misleadingly TARGET_URI before!
- public static final int IO_URI = 3;
+ public static final int IO_URI = 2;
+ public static final int IO_URIS = 3;
+
+ public static final String SELECTED_URI = "selected_uri";
// encrypt
public static final String ENCRYPT_SIGNATURE_KEY_ID = "secret_key_id";
@@ -121,8 +120,10 @@ public class KeychainIntentService extends IntentService
public static final String ENCRYPT_MESSAGE_BYTES = "message_bytes";
public static final String ENCRYPT_INPUT_FILE = "input_file";
public static final String ENCRYPT_INPUT_URI = "input_uri";
+ public static final String ENCRYPT_INPUT_URIS = "input_uris";
public static final String ENCRYPT_OUTPUT_FILE = "output_file";
public static final String ENCRYPT_OUTPUT_URI = "output_uri";
+ public static final String ENCRYPT_OUTPUT_URIS = "output_uris";
public static final String ENCRYPT_SYMMETRIC_PASSPHRASE = "passphrase";
// decrypt/verify
@@ -142,6 +143,7 @@ public class KeychainIntentService extends IntentService
// export key
public static final String EXPORT_OUTPUT_STREAM = "export_output_stream";
public static final String EXPORT_FILENAME = "export_filename";
+ public static final String EXPORT_URI = "export_uri";
public static final String EXPORT_SECRET = "export_secret";
public static final String EXPORT_ALL = "export_all";
public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
@@ -226,6 +228,7 @@ public class KeychainIntentService extends IntentService
try {
/* Input */
int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
+ Bundle resultData = new Bundle();
long signatureKeyId = data.getLong(ENCRYPT_SIGNATURE_KEY_ID);
String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE);
@@ -233,43 +236,47 @@ public class KeychainIntentService extends IntentService
boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_ARMOR);
long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS);
int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID);
- InputData inputData = createEncryptInputData(data);
- OutputStream outStream = createCryptOutputStream(data);
-
- /* Operation */
- PgpSignEncrypt.Builder builder =
- new PgpSignEncrypt.Builder(
- new ProviderHelper(this),
- PgpHelper.getFullVersion(this),
- inputData, outStream);
- builder.setProgressable(this);
+ int urisCount = data.containsKey(ENCRYPT_INPUT_URIS) ? data.getParcelableArrayList(ENCRYPT_INPUT_URIS).size() : 1;
+ for (int i = 0; i < urisCount; i++) {
+ data.putInt(SELECTED_URI, i);
+ InputData inputData = createEncryptInputData(data);
+ OutputStream outStream = createCryptOutputStream(data);
+
+ /* Operation */
+ PgpSignEncrypt.Builder builder =
+ new PgpSignEncrypt.Builder(
+ new ProviderHelper(this),
+ PgpHelper.getFullVersion(this),
+ inputData, outStream);
+ builder.setProgressable(this);
+
+ builder.setEnableAsciiArmorOutput(useAsciiArmor)
+ .setCompressionId(compressionId)
+ .setSymmetricEncryptionAlgorithm(
+ Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
+ .setEncryptionMasterKeyIds(encryptionKeyIds)
+ .setSymmetricPassphrase(symmetricPassphrase)
+ .setSignatureMasterKeyId(signatureKeyId)
+ .setEncryptToSigner(true)
+ .setSignatureHashAlgorithm(
+ Preferences.getPreferences(this).getDefaultHashAlgorithm())
+ .setSignaturePassphrase(
+ PassphraseCacheService.getCachedPassphrase(this, signatureKeyId));
+
+ // this assumes that the bytes are cleartext (valid for current implementation!)
+ if (source == IO_BYTES) {
+ builder.setCleartextInput(true);
+ }
- builder.setEnableAsciiArmorOutput(useAsciiArmor)
- .setCompressionId(compressionId)
- .setSymmetricEncryptionAlgorithm(
- Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
- .setEncryptionMasterKeyIds(encryptionKeyIds)
- .setSymmetricPassphrase(symmetricPassphrase)
- .setSignatureMasterKeyId(signatureKeyId)
- .setEncryptToSigner(true)
- .setSignatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .setSignaturePassphrase(
- PassphraseCacheService.getCachedPassphrase(this, signatureKeyId));
-
- // this assumes that the bytes are cleartext (valid for current implementation!)
- if (source == IO_BYTES) {
- builder.setCleartextInput(true);
- }
+ builder.build().execute();
- builder.build().execute();
+ outStream.close();
- outStream.close();
+ /* Output */
- /* Output */
+ finalizeEncryptOutputStream(data, resultData, outStream);
- Bundle resultData = new Bundle();
- finalizeEncryptOutputStream(data, resultData, outStream);
+ }
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -415,13 +422,16 @@ public class KeychainIntentService extends IntentService
boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
String outputFile = data.getString(EXPORT_FILENAME);
+ Uri outputUri = data.getParcelable(EXPORT_URI);
// If not exporting all keys get the masterKeyIds of the keys to export from the intent
boolean exportAll = data.getBoolean(EXPORT_ALL);
- // check if storage is ready
- if (!FileHelper.isStorageMounted(outputFile)) {
- throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
+ if (outputFile != null) {
+ // check if storage is ready
+ if (!FileHelper.isStorageMounted(outputFile)) {
+ throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
+ }
}
ArrayList<Long> publicMasterKeyIds = new ArrayList<Long>();
@@ -453,12 +463,19 @@ public class KeychainIntentService extends IntentService
}
}
+ OutputStream outStream;
+ if (outputFile != null) {
+ outStream = new FileOutputStream(outputFile);
+ } else {
+ outStream = getContentResolver().openOutputStream(outputUri);
+ }
+
PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);
Bundle resultData = pgpImportExport
.exportKeyRings(publicMasterKeyIds, secretMasterKeyIds,
- new FileOutputStream(outputFile));
+ outStream);
- if (mIsCanceled) {
+ if (mIsCanceled && outputFile != null) {
new File(outputFile).delete();
}
@@ -694,22 +711,17 @@ public class KeychainIntentService extends IntentService
byte[] bytes = data.getByteArray(bytesName);
return new InputData(new ByteArrayInputStream(bytes), bytes.length);
- case IO_FILE: /* encrypting file */
- String inputFile = data.getString(ENCRYPT_INPUT_FILE);
-
- // check if storage is ready
- if (!FileHelper.isStorageMounted(inputFile)) {
- throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
- }
-
- return new InputData(new FileInputStream(inputFile), new File(inputFile).length());
-
case IO_URI: /* encrypting content uri */
Uri providerUri = data.getParcelable(ENCRYPT_INPUT_URI);
// InputStream
- InputStream in = getContentResolver().openInputStream(providerUri);
- return new InputData(in, 0);
+ return new InputData(getContentResolver().openInputStream(providerUri), FileHelper.getFileSize(this, providerUri, 0));
+
+ case IO_URIS:
+ providerUri = data.<Uri>getParcelableArrayList(ENCRYPT_INPUT_URIS).get(data.getInt(SELECTED_URI));
+
+ // InputStream
+ return new InputData(getContentResolver().openInputStream(providerUri), FileHelper.getFileSize(this, providerUri, 0));
default:
throw new PgpGeneralException("No target choosen!");
@@ -722,23 +734,16 @@ public class KeychainIntentService extends IntentService
case IO_BYTES:
return new ByteArrayOutputStream();
- case IO_FILE:
- String outputFile = data.getString(ENCRYPT_OUTPUT_FILE);
-
- // check if storage is ready
- if (!FileHelper.isStorageMounted(outputFile)) {
- throw new PgpGeneralException(
- getString(R.string.error_external_storage_not_ready));
- }
-
- // OutputStream
- return new FileOutputStream(outputFile);
-
case IO_URI:
Uri providerUri = data.getParcelable(ENCRYPT_OUTPUT_URI);
return getContentResolver().openOutputStream(providerUri);
+ case IO_URIS:
+ providerUri = data.<Uri>getParcelableArrayList(ENCRYPT_OUTPUT_URIS).get(data.getInt(SELECTED_URI));
+
+ return getContentResolver().openOutputStream(providerUri);
+
default:
throw new PgpGeneralException("No target choosen!");
}
@@ -759,11 +764,8 @@ public class KeychainIntentService extends IntentService
byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
resultData.putByteArray(bytesName, output);
break;
- case IO_FILE:
- // nothing, file was written, just send okay and verification bundle
-
- break;
case IO_URI:
+ case IO_URIS:
// nothing, output was written, just send okay and verification bundle
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
index c27b3f6da..d7d98fd68 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
@@ -1,3 +1,20 @@
+/*
+ * 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.service;
import android.app.Activity;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
index 11829e7b8..543b83edb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
@@ -1,3 +1,20 @@
+/*
+ * 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.service;
import android.app.Activity;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 97d92dbf7..e813ca26c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -34,9 +34,8 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-
-import android.support.v4.util.LongSparseArray;
import android.support.v4.app.NotificationCompat;
+import android.support.v4.util.LongSparseArray;
import org.spongycastle.bcpg.S2K;
import org.sufficientlysecure.keychain.Constants;
@@ -396,8 +395,8 @@ public class PassphraseCacheService extends Service {
} else {
// Fallback, since expandable notifications weren't available back then
builder.setSmallIcon(R.drawable.ic_launcher)
- .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys,
- mPassphraseCache.size())))
+ .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys),
+ mPassphraseCache.size()))
.setContentText(getString(R.string.passp_cache_notif_click_to_clear));
Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 5e90b396e..490a8e738 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -1,3 +1,20 @@
+/*
+ * 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.service;
import android.os.Parcel;
@@ -6,16 +23,17 @@ import android.os.Parcelable;
import java.io.Serializable;
import java.util.ArrayList;
-/** This class is a a transferable representation for a collection of changes
+/**
+ * This class is a a transferable representation for a collection of changes
* to be done on a keyring.
- *
+ * <p/>
* This class should include all types of operations supported in the backend.
- *
+ * <p/>
* All changes are done in a differential manner. Besides the two key
* identification attributes, all attributes may be null, which indicates no
* change to the keyring. This is also the reason why boxed values are used
* instead of primitives in the subclasses.
- *
+ * <p/>
* Application of operations in the backend should be fail-fast, which means an
* error in any included operation (for example revocation of a non-existent
* subkey) will cause the operation as a whole to fail.
@@ -65,12 +83,23 @@ public class SaveKeyringParcel implements Parcelable {
public int mKeysize;
public int mFlags;
public Long mExpiry;
+
public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) {
mAlgorithm = algorithm;
mKeysize = keysize;
mFlags = flags;
mExpiry = expiry;
}
+
+ @Override
+ public String toString() {
+ String out = "mAlgorithm: " + mAlgorithm + ", ";
+ out += "mKeysize: " + mKeysize + ", ";
+ out += "mFlags: " + mFlags;
+ out += "mExpiry: " + mExpiry;
+
+ return out;
+ }
}
public static class SubkeyChange implements Serializable {
@@ -78,11 +107,46 @@ public class SaveKeyringParcel implements Parcelable {
public Integer mFlags;
// this is a long unix timestamp, in seconds (NOT MILLISECONDS!)
public Long mExpiry;
+
+ public SubkeyChange(long keyId) {
+ mKeyId = keyId;
+ }
+
public SubkeyChange(long keyId, Integer flags, Long expiry) {
mKeyId = keyId;
mFlags = flags;
mExpiry = expiry;
}
+
+ @Override
+ public String toString() {
+ String out = "mKeyId: " + mKeyId + ", ";
+ out += "mFlags: " + mFlags + ", ";
+ out += "mExpiry: " + mExpiry;
+
+ return out;
+ }
+ }
+
+ public SubkeyChange getSubkeyChange(long keyId) {
+ for (SubkeyChange subkeyChange : mChangeSubKeys) {
+ if (subkeyChange.mKeyId == keyId) {
+ return subkeyChange;
+ }
+ }
+ return null;
+ }
+
+ public SubkeyChange getOrCreateSubkeyChange(long keyId) {
+ SubkeyChange foundSubkeyChange = getSubkeyChange(keyId);
+ if (foundSubkeyChange != null) {
+ return foundSubkeyChange;
+ } else {
+ // else, create a new one
+ SubkeyChange newSubkeyChange = new SubkeyChange(keyId);
+ mChangeSubKeys.add(newSubkeyChange);
+ return newSubkeyChange;
+ }
}
public SaveKeyringParcel(Parcel source) {
@@ -104,7 +168,7 @@ public class SaveKeyringParcel implements Parcelable {
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeInt(mMasterKeyId == null ? 0 : 1);
- if(mMasterKeyId != null) {
+ if (mMasterKeyId != null) {
destination.writeLong(mMasterKeyId);
}
destination.writeByteArray(mFingerprint);
@@ -136,4 +200,17 @@ public class SaveKeyringParcel implements Parcelable {
return 0;
}
+ @Override
+ public String toString() {
+ String out = "mMasterKeyId: " + mMasterKeyId + "\n";
+ out += "mNewPassphrase: " + mNewPassphrase + "\n";
+ out += "mAddUserIds: " + mAddUserIds + "\n";
+ out += "mAddSubKeys: " + mAddSubKeys + "\n";
+ out += "mChangeSubKeys: " + mChangeSubKeys + "\n";
+ out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n";
+ out += "mRevokeUserIds: " + mRevokeUserIds + "\n";
+ out += "mRevokeSubKeys: " + mRevokeSubKeys;
+
+ return out;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
index 662ba4ce1..773be816a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -31,8 +31,8 @@ import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.KeyFlags;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.KeyRing;
@@ -169,11 +169,12 @@ public class CreateKeyFinalFragment extends Fragment {
Bundle data = new Bundle();
SaveKeyringParcel parcel = new SaveKeyringParcel();
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.CERTIFY_OTHER, null));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.SIGN_DATA, null));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.SIGN_DATA, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, null));
String userId = KeyRing.createUserId(mName, mEmail, null);
parcel.mAddUserIds.add(userId);
+ parcel.mChangePrimaryUserId = userId;
parcel.mNewPassphrase = mPassphrase;
// get selected key entries
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index dba742268..830b9a279 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -23,11 +23,9 @@ import android.net.Uri;
import android.os.Bundle;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
-import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.util.Log;
@@ -114,7 +112,7 @@ public class DecryptActivity extends DrawerActivity {
} else {
// Binary via content provider (could also be files)
// override uri to get stream from send
- uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
+ uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
action = ACTION_DECRYPT;
}
} else if (Intent.ACTION_VIEW.equals(action)) {
@@ -122,6 +120,7 @@ public class DecryptActivity extends DrawerActivity {
// override action
action = ACTION_DECRYPT;
+ mFileFragmentBundle.putBoolean(DecryptFileFragment.ARG_FROM_VIEW_INTENT, true);
}
String textData = extras.getString(EXTRA_TEXT);
@@ -155,21 +154,8 @@ public class DecryptActivity extends DrawerActivity {
}
}
} else if (ACTION_DECRYPT.equals(action) && uri != null) {
- // get file path from uri
- String path = FileHelper.getPath(this, uri);
-
- if (path != null) {
- mFileFragmentBundle.putString(DecryptFileFragment.ARG_FILENAME, path);
- mSwitchToTab = PAGER_TAB_FILE;
- } else {
- Log.e(Constants.TAG,
- "Direct binary data without actual file in filesystem is not supported. " +
- "Please use the Remote Service API!");
- Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
- .show();
- // end activity
- finish();
- }
+ mFileFragmentBundle.putParcelable(DecryptFileFragment.ARG_URI, uri);
+ mSwitchToTab = PAGER_TAB_FILE;
} else if (ACTION_DECRYPT.equals(action)) {
Log.e(Constants.TAG,
"Include the extra 'text' or an Uri with setData() in your Intent!");
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
index 56dfdbd95..c33b1489a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
@@ -20,19 +20,16 @@ package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
-import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
-import android.provider.OpenableColumns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.ImageButton;
+import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -40,31 +37,28 @@ import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.util.Notify;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.io.File;
public class DecryptFileFragment extends DecryptFragment {
- public static final String ARG_FILENAME = "filename";
+ public static final String ARG_URI = "uri";
+ public static final String ARG_FROM_VIEW_INTENT = "view_intent";
- private static final int RESULT_CODE_FILE = 0x00007003;
+ private static final int REQUEST_CODE_INPUT = 0x00007003;
+ private static final int REQUEST_CODE_OUTPUT = 0x00007007;
// view
- private EditText mFilename;
+ private TextView mFilename;
private CheckBox mDeleteAfter;
- private ImageButton mBrowse;
private View mDecryptButton;
- private String mInputFilename = null;
+ // model
private Uri mInputUri = null;
- private String mOutputFilename = null;
private Uri mOutputUri = null;
- private FileDialogFragment mFileDialog;
-
/**
* Inflate the layout for this fragment
*/
@@ -72,17 +66,16 @@ public class DecryptFileFragment extends DecryptFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.decrypt_file_fragment, container, false);
- mFilename = (EditText) view.findViewById(R.id.decrypt_file_filename);
- mBrowse = (ImageButton) view.findViewById(R.id.decrypt_file_browse);
+ mFilename = (TextView) view.findViewById(R.id.decrypt_file_filename);
mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption);
mDecryptButton = view.findViewById(R.id.decrypt_file_action_decrypt);
- mBrowse.setOnClickListener(new View.OnClickListener() {
+ view.findViewById(R.id.decrypt_file_browse).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
- if (Constants.KITKAT) {
- FileHelper.openDocument(DecryptFileFragment.this, mInputUri, "*/*", RESULT_CODE_FILE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ FileHelper.openDocument(DecryptFileFragment.this, "*/*", REQUEST_CODE_INPUT);
} else {
- FileHelper.openFile(DecryptFileFragment.this, mFilename.getText().toString(), "*/*",
- RESULT_CODE_FILE);
+ FileHelper.openFile(DecryptFileFragment.this, mInputUri, "*/*",
+ REQUEST_CODE_INPUT);
}
}
});
@@ -100,74 +93,47 @@ public class DecryptFileFragment extends DecryptFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- String filename = getArguments().getString(ARG_FILENAME);
- if (filename != null) {
- mFilename.setText(filename);
- }
+ setInputUri(getArguments().<Uri>getParcelable(ARG_URI));
}
- private String guessOutputFilename() {
- File file = new File(mInputFilename);
- String filename = file.getName();
- if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) {
- filename = filename.substring(0, filename.length() - 4);
+ private void setInputUri(Uri inputUri) {
+ if (inputUri == null) {
+ mInputUri = null;
+ mFilename.setText("");
+ return;
}
- return Constants.Path.APP_DIR + "/" + filename;
+
+ mInputUri = inputUri;
+ mFilename.setText(FileHelper.getFilename(getActivity(), mInputUri));
}
private void decryptAction() {
- String currentFilename = mFilename.getText().toString();
- if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
- mInputUri = null;
- mInputFilename = mFilename.getText().toString();
- }
-
if (mInputUri == null) {
- mOutputFilename = guessOutputFilename();
- }
-
- if (mInputFilename.equals("")) {
Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
return;
}
- if (mInputUri == null && mInputFilename.startsWith("file")) {
- File file = new File(mInputFilename);
- if (!file.exists() || !file.isFile()) {
- Notify.showNotify(getActivity(), getString(R.string.error_message,
- getString(R.string.error_file_not_found)), Notify.Style.ERROR);
- return;
- }
- }
-
askForOutputFilename();
}
- private void askForOutputFilename() {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == FileDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- if (data.containsKey(FileDialogFragment.MESSAGE_DATA_URI)) {
- mOutputUri = data.getParcelable(FileDialogFragment.MESSAGE_DATA_URI);
- } else {
- mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
- }
- decryptStart(null);
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- mFileDialog = FileDialogFragment.newInstance(messenger,
- getString(R.string.title_decrypt_to_file),
- getString(R.string.specify_file_to_decrypt_to), mOutputFilename, null);
+ private String removeEncryptedAppend(String name) {
+ if (name.endsWith(".asc") || name.endsWith(".gpg") || name.endsWith(".pgp")) {
+ return name.substring(0, name.length() - 4);
+ }
+ return name;
+ }
- mFileDialog.show(getActivity().getSupportFragmentManager(), "fileDialog");
+ private void askForOutputFilename() {
+ String targetName = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri));
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ File file = new File(mInputUri.getPath());
+ File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
+ File targetFile = new File(parentDir, targetName);
+ FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file),
+ getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT);
+ } else {
+ FileHelper.saveDocument(this, "*/*", targetName, REQUEST_CODE_OUTPUT);
+ }
}
@Override
@@ -183,25 +149,13 @@ public class DecryptFileFragment extends DecryptFragment {
intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
// data
- Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
- + mOutputFilename + ",mInputUri=" + mInputUri + ", mOutputUri="
- + mOutputUri);
+ Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri);
- if (mInputUri != null) {
- data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI);
- data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri);
- } else {
- data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_FILE);
- data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
- }
+ data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI);
+ data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri);
- if (mOutputUri != null) {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI);
- data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri);
- } else {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_FILE);
- data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
- }
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI);
+ data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri);
data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase);
@@ -232,15 +186,19 @@ public class DecryptFileFragment extends DecryptFragment {
if (mDeleteAfter.isChecked()) {
// Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog;
- if (mInputUri != null) {
- deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);
- } else {
- deleteFileDialog = DeleteFileDialogFragment
- .newInstance(mInputFilename);
- }
+ DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);
deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
+ setInputUri(null);
}
+
+ /*
+ // A future open after decryption feature
+ if () {
+ Intent viewFile = new Intent(Intent.ACTION_VIEW);
+ viewFile.setData(mOutputUri);
+ startActivity(viewFile);
+ }
+ */
}
}
}
@@ -260,28 +218,17 @@ public class DecryptFileFragment extends DecryptFragment {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
- case RESULT_CODE_FILE: {
+ case REQUEST_CODE_INPUT: {
if (resultCode == Activity.RESULT_OK && data != null) {
- if (Constants.KITKAT) {
- mInputUri = data.getData();
- Cursor cursor = getActivity().getContentResolver().query(mInputUri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
- if (cursor != null) {
- if (cursor.moveToNext()) {
- mInputFilename = cursor.getString(0);
- mFilename.setText(mInputFilename);
- }
- cursor.close();
- }
- } else {
- try {
- String path = FileHelper.getPath(getActivity(), data.getData());
- Log.d(Constants.TAG, "path=" + path);
-
- mFilename.setText(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!");
- }
- }
+ setInputUri(data.getData());
+ }
+ return;
+ }
+ case REQUEST_CODE_OUTPUT: {
+ // This happens after output file was selected, so start our operation
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ mOutputUri = data.getData();
+ decryptStart(null);
}
return;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
index d4235b82b..211a20ec8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -25,11 +25,11 @@ import android.os.Message;
import android.support.v4.app.Fragment;
import android.view.View;
import android.view.View.OnClickListener;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import android.widget.Button;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.R;
@@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
-public class DecryptFragment extends Fragment {
+public abstract class DecryptFragment extends Fragment {
private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
protected long mSignatureKeyId = 0;
@@ -217,8 +217,6 @@ public class DecryptFragment extends Fragment {
*
* @param passphrase
*/
- protected void decryptStart(String passphrase) {
-
- }
+ protected abstract void decryptStart(String passphrase);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
index 586442bb0..0e8d6f39d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -35,9 +35,9 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
-import android.widget.ImageView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index b76755bb2..5be196a45 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -37,7 +37,6 @@ import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
-import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
@@ -57,17 +56,15 @@ import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ChangeExpiryDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
-import java.util.ArrayList;
-import java.util.Date;
-
public class EditKeyFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
@@ -95,8 +92,8 @@ public class EditKeyFragment extends LoaderFragment implements
private Uri mDataUri;
private SaveKeyringParcel mSaveKeyringParcel;
- private String mPrimaryUserId;
+ private String mPrimaryUserId;
private String mCurrentPassphrase;
/**
@@ -215,8 +212,7 @@ public class EditKeyFragment extends LoaderFragment implements
mUserIdsList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- String userId = mUserIdsAdapter.getUserId(position);
- editUserId(userId);
+ editUserId(position);
}
});
@@ -230,8 +226,7 @@ public class EditKeyFragment extends LoaderFragment implements
mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- long keyId = mSubkeysAdapter.getKeyId(position);
- editSubkey(keyId);
+ editSubkey(position);
}
});
@@ -320,7 +315,11 @@ public class EditKeyFragment extends LoaderFragment implements
setPassphraseDialog.show(getActivity().getSupportFragmentManager(), "setPassphraseDialog");
}
- private void editUserId(final String userId) {
+ private void editUserId(final int position) {
+ final String userId = mUserIdsAdapter.getUserId(position);
+ final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position);
+ final boolean isRevokedPending = mUserIdsAdapter.getIsRevokedPending(position);
+
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
@@ -353,20 +352,21 @@ public class EditKeyFragment extends LoaderFragment implements
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
EditUserIdDialogFragment dialogFragment =
- EditUserIdDialogFragment.newInstance(messenger);
-
+ EditUserIdDialogFragment.newInstance(messenger, isRevoked, isRevokedPending);
dialogFragment.show(getActivity().getSupportFragmentManager(), "editUserIdDialog");
}
});
}
- private void editSubkey(final long keyId) {
+ private void editSubkey(final int position) {
+ final long keyId = mSubkeysAdapter.getKeyId(position);
+
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case EditSubkeyDialogFragment.MESSAGE_CHANGE_EXPIRY:
- editSubkeyExpiry(keyId);
+ editSubkeyExpiry(position);
break;
case EditSubkeyDialogFragment.MESSAGE_REVOKE:
// toggle
@@ -394,19 +394,19 @@ public class EditKeyFragment extends LoaderFragment implements
});
}
- private void editSubkeyExpiry(final long keyId) {
+ private void editSubkeyExpiry(final int position) {
+ final long keyId = mSubkeysAdapter.getKeyId(position);
+ final Long creationDate = mSubkeysAdapter.getCreationDate(position);
+ final Long expiryDate = mSubkeysAdapter.getExpiryDate(position);
+
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
- case ChangeExpiryDialogFragment.MESSAGE_NEW_EXPIRY_DATE:
- // toggle
-// if (mSaveKeyringParcel.changePrimaryUserId != null
-// && mSaveKeyringParcel.changePrimaryUserId.equals(userId)) {
-// mSaveKeyringParcel.changePrimaryUserId = null;
-// } else {
-// mSaveKeyringParcel.changePrimaryUserId = userId;
-// }
+ case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY_DATE:
+ Long expiry = (Long) message.getData().
+ getSerializable(EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY_DATE);
+ mSaveKeyringParcel.getOrCreateSubkeyChange(keyId).mExpiry = expiry;
break;
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
@@ -418,8 +418,8 @@ public class EditKeyFragment extends LoaderFragment implements
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
- ChangeExpiryDialogFragment dialogFragment =
- ChangeExpiryDialogFragment.newInstance(messenger, new Date(), new Date());
+ EditSubkeyExpiryDialogFragment dialogFragment =
+ EditSubkeyExpiryDialogFragment.newInstance(messenger, creationDate, expiryDate);
dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyExpiryDialog");
}
@@ -453,8 +453,21 @@ public class EditKeyFragment extends LoaderFragment implements
}
private void addSubkey() {
- // default values
- mSubkeysAddedAdapter.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.SIGN_DATA, null));
+ boolean willBeMasterKey = mSubkeysAdapter.getCount() == 0
+ && mSubkeysAddedAdapter.getCount() == 0;
+
+ AddSubkeyDialogFragment addSubkeyDialogFragment =
+ AddSubkeyDialogFragment.newInstance(willBeMasterKey);
+ addSubkeyDialogFragment
+ .setOnAlgorithmSelectedListener(
+ new AddSubkeyDialogFragment.OnAlgorithmSelectedListener() {
+ @Override
+ public void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey) {
+ mSubkeysAddedAdapter.add(newSubkey);
+ }
+ }
+ );
+ addSubkeyDialogFragment.show(getActivity().getSupportFragmentManager(), "addSubkeyDialog");
}
private void cachePassphraseForEdit() {
@@ -479,9 +492,7 @@ public class EditKeyFragment extends LoaderFragment implements
}
private void save(String passphrase) {
- Log.d(Constants.TAG, "mSaveKeyringParcel.mAddUserIds: " + mSaveKeyringParcel.mAddUserIds);
- Log.d(Constants.TAG, "mSaveKeyringParcel.mNewPassphrase: " + mSaveKeyringParcel.mNewPassphrase);
- Log.d(Constants.TAG, "mSaveKeyringParcel.mRevokeUserIds: " + mSaveKeyringParcel.mRevokeUserIds);
+ Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString());
// Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index cc69148c1..94f828b48 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -18,23 +18,46 @@
package org.sufficientlysecure.keychain.ui;
+import android.app.ProgressDialog;
import android.content.Intent;
+import android.content.pm.LabeledIntent;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.support.v4.app.Fragment;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
-import android.widget.Toast;
+import android.view.Menu;
+import android.view.MenuItem;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.FileHelper;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
-public class EncryptActivity extends DrawerActivity implements
- EncryptSymmetricFragment.OnSymmetricKeySelection,
- EncryptAsymmetricFragment.OnAsymmetricKeySelection,
- EncryptActivityInterface {
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class EncryptActivity extends DrawerActivity implements EncryptActivityInterface {
/* Intents */
public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT";
@@ -51,7 +74,7 @@ public class EncryptActivity extends DrawerActivity implements
// view
ViewPager mViewPagerMode;
- PagerTabStrip mPagerTabStripMode;
+ //PagerTabStrip mPagerTabStripMode;
PagerTabStripAdapter mTabsAdapterMode;
ViewPager mViewPagerContent;
PagerTabStrip mPagerTabStripContent;
@@ -72,63 +95,386 @@ public class EncryptActivity extends DrawerActivity implements
// model used by message and file fragments
private long mEncryptionKeyIds[] = null;
+ private String mEncryptionUserIds[] = null;
private long mSigningKeyId = Constants.key.none;
- private String mPassphrase;
- private String mPassphraseAgain;
+ private String mPassphrase = "";
+ private boolean mUseArmor;
+ private boolean mDeleteAfterEncrypt = false;
+ private boolean mShareAfterEncrypt = false;
+ private ArrayList<Uri> mInputUris;
+ private ArrayList<Uri> mOutputUris;
+ private String mMessage = "";
+
+ public boolean isModeSymmetric() {
+ return PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem();
+ }
+
+ public boolean isContentMessage() {
+ return PAGER_CONTENT_MESSAGE == mViewPagerContent.getCurrentItem();
+ }
@Override
- public void onSigningKeySelected(long signingKeyId) {
- mSigningKeyId = signingKeyId;
+ public boolean isUseArmor() {
+ return mUseArmor;
}
@Override
- public void onEncryptionKeysSelected(long[] encryptionKeyIds) {
- mEncryptionKeyIds = encryptionKeyIds;
+ public long getSignatureKey() {
+ return mSigningKeyId;
}
@Override
- public void onPassphraseUpdate(String passphrase) {
+ public long[] getEncryptionKeys() {
+ return mEncryptionKeyIds;
+ }
+
+ @Override
+ public String[] getEncryptionUsers() {
+ return mEncryptionUserIds;
+ }
+
+ @Override
+ public void setSignatureKey(long signatureKey) {
+ mSigningKeyId = signatureKey;
+ notifyUpdate();
+ }
+
+ @Override
+ public void setEncryptionKeys(long[] encryptionKeys) {
+ mEncryptionKeyIds = encryptionKeys;
+ notifyUpdate();
+ }
+
+ @Override
+ public void setEncryptionUsers(String[] encryptionUsers) {
+ mEncryptionUserIds = encryptionUsers;
+ notifyUpdate();
+ }
+
+ @Override
+ public void setPassphrase(String passphrase) {
mPassphrase = passphrase;
}
@Override
- public void onPassphraseAgainUpdate(String passphrase) {
- mPassphraseAgain = passphrase;
+ public ArrayList<Uri> getInputUris() {
+ if (mInputUris == null) mInputUris = new ArrayList<Uri>();
+ return mInputUris;
}
@Override
- public boolean isModeSymmetric() {
- if (PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem()) {
- return true;
- } else {
- return false;
- }
+ public ArrayList<Uri> getOutputUris() {
+ if (mOutputUris == null) mOutputUris = new ArrayList<Uri>();
+ return mOutputUris;
}
@Override
- public long getSignatureKey() {
- return mSigningKeyId;
+ public void setInputUris(ArrayList<Uri> uris) {
+ mInputUris = uris;
+ notifyUpdate();
}
@Override
- public long[] getEncryptionKeys() {
- return mEncryptionKeyIds;
+ public void setOutputUris(ArrayList<Uri> uris) {
+ mOutputUris = uris;
+ notifyUpdate();
+ }
+
+ @Override
+ public String getMessage() {
+ return mMessage;
}
@Override
- public String getPassphrase() {
- return mPassphrase;
+ public void setMessage(String message) {
+ mMessage = message;
}
@Override
- public String getPassphraseAgain() {
- return mPassphraseAgain;
+ public void notifyUpdate() {
+ for (Fragment fragment : getSupportFragmentManager().getFragments()) {
+ if (fragment instanceof EncryptActivityInterface.UpdateListener) {
+ ((UpdateListener) fragment).onNotifyUpdate();
+ }
+ }
}
+ @Override
+ public void startEncrypt(boolean share) {
+ mShareAfterEncrypt = share;
+ startEncrypt();
+ }
+
+ public void startEncrypt() {
+ if (!inputIsValid()) {
+ // Notify was created by inputIsValid.
+ return;
+ }
+
+ // Send all information needed to service to edit key in other thread
+ Intent intent = new Intent(this, KeychainIntentService.class);
+ intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, createEncryptBundle());
+
+ // Message is received after encrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler(this,
+ getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ if (!isContentMessage()) {
+ Notify.showNotify(EncryptActivity.this, R.string.encrypt_sign_successful, Notify.Style.INFO);
+
+ if (mDeleteAfterEncrypt) {
+ for (Uri inputUri : mInputUris) {
+ DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(inputUri);
+ deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
+ }
+ mInputUris.clear();
+ notifyUpdate();
+ }
+ }
+
+ if (mShareAfterEncrypt) {
+ // Share encrypted message/file
+ startActivity(sendWithChooserExcludingEncrypt(message));
+ } else if (isContentMessage()) {
+ // Copy to clipboard
+ copyToClipboard(message);
+ Notify.showNotify(EncryptActivity.this,
+ R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
+ }
+ }
+ }
+ };
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(serviceHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ serviceHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+ }
+
+ private Bundle createEncryptBundle() {
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ if (isContentMessage()) {
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES);
+ data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mMessage.getBytes());
+ } else {
+ data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URIS);
+ data.putParcelableArrayList(KeychainIntentService.ENCRYPT_INPUT_URIS, mInputUris);
+
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URIS);
+ data.putParcelableArrayList(KeychainIntentService.ENCRYPT_OUTPUT_URIS, mOutputUris);
+ }
+
+ // Always use armor for messages
+ data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, mUseArmor || isContentMessage());
+
+ // TODO: Only default compression right now...
+ int compressionId = Preferences.getPreferences(this).getDefaultMessageCompression();
+ data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
+
+ if (isModeSymmetric()) {
+ Log.d(Constants.TAG, "Symmetric encryption enabled!");
+ String passphrase = mPassphrase;
+ if (passphrase.length() == 0) {
+ passphrase = null;
+ }
+ data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
+ } else {
+ data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_ID, mSigningKeyId);
+ data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, mEncryptionKeyIds);
+ }
+ return data;
+ }
+
+ private void copyToClipboard(Message message) {
+ ClipboardReflection.copyToClipboard(this, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES)));
+ }
+
+ /**
+ * Create Intent Chooser but exclude OK's EncryptActivity.
+ * <p/>
+ * Put together from some stackoverflow posts...
+ *
+ * @param message
+ * @return
+ */
+ private Intent sendWithChooserExcludingEncrypt(Message message) {
+ Intent prototype = createSendIntent(message);
+
+ String title = isContentMessage() ? getString(R.string.title_share_message)
+ : getString(R.string.title_share_file);
+
+ // fallback on Android 2.3, otherwise we would get weird results
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return Intent.createChooser(prototype, title);
+ }
+
+ // prevent recursion aka Inception :P
+ String[] blacklist = new String[]{Constants.PACKAGE_NAME + ".ui.EncryptActivity"};
+
+ List<LabeledIntent> targetedShareIntents = new ArrayList<LabeledIntent>();
+
+ List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(prototype, 0);
+ List<ResolveInfo> resInfoListFiltered = new ArrayList<ResolveInfo>();
+ if (!resInfoList.isEmpty()) {
+ for (ResolveInfo resolveInfo : resInfoList) {
+ // do not add blacklisted ones
+ if (resolveInfo.activityInfo == null || Arrays.asList(blacklist).contains(resolveInfo.activityInfo.name))
+ continue;
+
+ resInfoListFiltered.add(resolveInfo);
+ }
+
+ if (!resInfoListFiltered.isEmpty()) {
+ // sorting for nice readability
+ Collections.sort(resInfoListFiltered, new Comparator<ResolveInfo>() {
+ @Override
+ public int compare(ResolveInfo first, ResolveInfo second) {
+ String firstName = first.loadLabel(getPackageManager()).toString();
+ String secondName = second.loadLabel(getPackageManager()).toString();
+ return firstName.compareToIgnoreCase(secondName);
+ }
+ });
+
+ // create the custom intent list
+ for (ResolveInfo resolveInfo : resInfoListFiltered) {
+ Intent targetedShareIntent = (Intent) prototype.clone();
+ targetedShareIntent.setPackage(resolveInfo.activityInfo.packageName);
+ targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
+
+ LabeledIntent lIntent = new LabeledIntent(targetedShareIntent,
+ resolveInfo.activityInfo.packageName,
+ resolveInfo.loadLabel(getPackageManager()),
+ resolveInfo.activityInfo.icon);
+ targetedShareIntents.add(lIntent);
+ }
+
+ // Create chooser with only one Intent in it
+ Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title);
+ // append all other Intents
+ chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{}));
+ return chooserIntent;
+ }
+
+ }
+
+ // fallback to Android's default chooser
+ return Intent.createChooser(prototype, title);
+ }
+
+ private Intent createSendIntent(Message message) {
+ Intent sendIntent;
+ if (isContentMessage()) {
+ sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.setType("text/plain");
+ sendIntent.putExtra(Intent.EXTRA_TEXT, new String(message.getData().getByteArray(KeychainIntentService.RESULT_BYTES)));
+ } else {
+ // file
+ if (mOutputUris.size() == 1) {
+ sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris.get(0));
+ } else {
+ sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
+ sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris);
+ }
+ sendIntent.setType("application/pgp-encrypted");
+ }
+ if (!isModeSymmetric() && mEncryptionUserIds != null) {
+ Set<String> users = new HashSet<String>();
+ for (String user : mEncryptionUserIds) {
+ String[] userId = KeyRing.splitUserId(user);
+ if (userId[1] != null) {
+ users.add(userId[1]);
+ }
+ }
+ sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()]));
+ }
+ return sendIntent;
+ }
+
+ private boolean inputIsValid() {
+ if (isContentMessage()) {
+ if (mMessage == null) {
+ Notify.showNotify(this, R.string.error_message, Notify.Style.ERROR);
+ return false;
+ }
+ } else {
+ // file checks
+
+ if (mInputUris.isEmpty()) {
+ Notify.showNotify(this, R.string.no_file_selected, Notify.Style.ERROR);
+ return false;
+ } else if (mInputUris.size() > 1 && !mShareAfterEncrypt) {
+ // This should be impossible...
+ return false;
+ } else if (mInputUris.size() != mOutputUris.size()) {
+ // This as well
+ return false;
+ }
+ }
+
+ if (isModeSymmetric()) {
+ // symmetric encryption checks
+
+
+ if (mPassphrase == null) {
+ Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR);
+ return false;
+ }
+ if (mPassphrase.isEmpty()) {
+ Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR);
+ return false;
+ }
+
+ } else {
+ // asymmetric encryption checks
+
+ boolean gotEncryptionKeys = (mEncryptionKeyIds != null
+ && mEncryptionKeyIds.length > 0);
+
+ // Files must be encrypted, only text can be signed-only right now
+ if (!gotEncryptionKeys && !isContentMessage()) {
+ Notify.showNotify(this, R.string.select_encryption_key, Notify.Style.ERROR);
+ return false;
+ }
+
+ if (!gotEncryptionKeys && mSigningKeyId == 0) {
+ Notify.showNotify(this, R.string.select_encryption_or_signature_key, Notify.Style.ERROR);
+ return false;
+ }
+
+ if (mSigningKeyId != 0 && PassphraseCacheService.getCachedPassphrase(this, mSigningKeyId) == null) {
+ PassphraseDialogFragment.show(this, mSigningKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ // restart
+ startEncrypt();
+ }
+ }
+ }
+ );
+
+ return false;
+ }
+ }
+ return true;
+ }
private void initView() {
mViewPagerMode = (ViewPager) findViewById(R.id.encrypt_pager_mode);
- mPagerTabStripMode = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_mode);
+ //mPagerTabStripMode = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_mode);
mViewPagerContent = (ViewPager) findViewById(R.id.encrypt_pager_content);
mPagerTabStripContent = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_content);
@@ -165,8 +511,43 @@ public class EncryptActivity extends DrawerActivity implements
mTabsAdapterContent.addTab(EncryptMessageFragment.class,
mMessageFragmentBundle, getString(R.string.label_message));
mTabsAdapterContent.addTab(EncryptFileFragment.class,
- mFileFragmentBundle, getString(R.string.label_file));
+ mFileFragmentBundle, getString(R.string.label_files));
mViewPagerContent.setCurrentItem(mSwitchToContent);
+
+ mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.encrypt_activity, menu);
+ menu.findItem(R.id.check_use_armor).setChecked(mUseArmor);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.isCheckable()) {
+ item.setChecked(!item.isChecked());
+ }
+ switch (item.getItemId()) {
+ case R.id.check_use_symmetric:
+ mSwitchToMode = item.isChecked() ? PAGER_MODE_SYMMETRIC : PAGER_MODE_ASYMMETRIC;
+
+ mViewPagerMode.setCurrentItem(mSwitchToMode);
+ notifyUpdate();
+ break;
+ case R.id.check_use_armor:
+ mUseArmor = item.isChecked();
+ notifyUpdate();
+ break;
+ case R.id.check_delete_after_encrypt:
+ mDeleteAfterEncrypt = item.isChecked();
+ notifyUpdate();
+ break;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ return true;
}
/**
@@ -178,17 +559,21 @@ public class EncryptActivity extends DrawerActivity implements
String action = intent.getAction();
Bundle extras = intent.getExtras();
String type = intent.getType();
- Uri uri = intent.getData();
+ ArrayList<Uri> uris = new ArrayList<Uri>();
if (extras == null) {
extras = new Bundle();
}
+ if (intent.getData() != null) {
+ uris.add(intent.getData());
+ }
+
/*
* Android's Action
*/
if (Intent.ACTION_SEND.equals(action) && type != null) {
- // When sending to APG Encrypt via share menu
+ // When sending to OpenKeychain Encrypt via share menu
if ("text/plain".equals(type)) {
// Plain text
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
@@ -201,14 +586,19 @@ public class EncryptActivity extends DrawerActivity implements
}
} else {
// Files via content provider, override uri and action
- uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
+ uris.clear();
+ uris.add(intent.<Uri>getParcelableExtra(Intent.EXTRA_STREAM));
action = ACTION_ENCRYPT;
}
}
+ if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
+ uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+ action = ACTION_ENCRYPT;
+ }
+
if (extras.containsKey(EXTRA_ASCII_ARMOR)) {
- boolean requestAsciiArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, true);
- mFileFragmentBundle.putBoolean(EncryptFileFragment.ARG_ASCII_ARMOR, requestAsciiArmor);
+ mUseArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, true);
}
String textData = extras.getString(EXTRA_TEXT);
@@ -230,25 +620,10 @@ public class EncryptActivity extends DrawerActivity implements
// encrypt text based on given extra
mMessageFragmentBundle.putString(EncryptMessageFragment.ARG_TEXT, textData);
mSwitchToContent = PAGER_CONTENT_MESSAGE;
- } else if (ACTION_ENCRYPT.equals(action) && uri != null) {
+ } else if (ACTION_ENCRYPT.equals(action) && uris != null && !uris.isEmpty()) {
// encrypt file based on Uri
-
- // get file path from uri
- String path = FileHelper.getPath(this, uri);
-
- if (path != null) {
- mFileFragmentBundle.putString(EncryptFileFragment.ARG_FILENAME, path);
- mSwitchToContent = PAGER_CONTENT_FILE;
- } else {
- Log.e(Constants.TAG,
- "Direct binary data without actual file in filesystem is not supported " +
- "by Intents. Please use the Remote Service API!"
- );
- Toast.makeText(this, R.string.error_only_files_are_supported,
- Toast.LENGTH_LONG).show();
- // end activity
- finish();
- }
+ mFileFragmentBundle.putParcelableArrayList(EncryptFileFragment.ARG_URIS, uris);
+ mSwitchToContent = PAGER_CONTENT_FILE;
} else if (ACTION_ENCRYPT.equals(action)) {
Log.e(Constants.TAG,
"Include the extra 'text' or an Uri with setData() in your Intent!");
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java
index 0786b3a16..54fe369a7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java
@@ -17,14 +17,41 @@
package org.sufficientlysecure.keychain.ui;
+import android.net.Uri;
+
+import java.util.ArrayList;
+
public interface EncryptActivityInterface {
- public boolean isModeSymmetric();
+ public interface UpdateListener {
+ void onNotifyUpdate();
+ }
+
+ public boolean isUseArmor();
public long getSignatureKey();
public long[] getEncryptionKeys();
+ public String[] getEncryptionUsers();
+ public void setSignatureKey(long signatureKey);
+ public void setEncryptionKeys(long[] encryptionKeys);
+ public void setEncryptionUsers(String[] encryptionUsers);
+
+ public void setPassphrase(String passphrase);
+
+ // ArrayList on purpose as only those are parcelable
+ public ArrayList<Uri> getInputUris();
+ public ArrayList<Uri> getOutputUris();
+ public void setInputUris(ArrayList<Uri> uris);
+ public void setOutputUris(ArrayList<Uri> uris);
+
+ public String getMessage();
+ public void setMessage(String message);
- public String getPassphrase();
- public String getPassphraseAgain();
+ /**
+ * Call this to notify the UI for changes done on the array lists or arrays,
+ * automatically called if setter is used
+ */
+ public void notifyUpdate();
+ public void startEncrypt(boolean share);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
index eb807792b..3de617ca0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -18,16 +18,25 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.content.Intent;
+import android.content.Context;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
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.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.CheckBox;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
import android.widget.TextView;
-import android.widget.Button;
+
+import com.tokenautocomplete.TokenCompleteTextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -37,61 +46,52 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.Notify;
-import java.util.Vector;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
-public class EncryptAsymmetricFragment extends Fragment {
+public class EncryptAsymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener {
public static final String ARG_SIGNATURE_KEY_ID = "signature_key_id";
public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids";
- public static final int REQUEST_CODE_PUBLIC_KEYS = 0x00007001;
- public static final int REQUEST_CODE_SECRET_KEYS = 0x00007002;
-
ProviderHelper mProviderHelper;
- OnAsymmetricKeySelection mKeySelectionListener;
-
// view
- private Button mSelectKeysButton;
- private CheckBox mSign;
- private TextView mMainUserId;
- private TextView mMainUserIdRest;
+ private Spinner mSign;
+ private EncryptKeyCompletionView mEncryptKeyView;
+ private SelectSignKeyCursorAdapter mSignAdapter = new SelectSignKeyCursorAdapter();
// model
- private long mSecretKeyId = Constants.key.none;
- private long mEncryptionKeyIds[] = null;
+ private EncryptActivityInterface mEncryptInterface;
- // Container Activity must implement this interface
- public interface OnAsymmetricKeySelection {
- public void onSigningKeySelected(long signingKeyId);
+ @Override
+ public void onNotifyUpdate() {
- public void onEncryptionKeysSelected(long[] encryptionKeyIds);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
- mKeySelectionListener = (OnAsymmetricKeySelection) activity;
+ mEncryptInterface = (EncryptActivityInterface) activity;
} catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement OnAsymmetricKeySelection");
+ throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface");
}
}
private void setSignatureKeyId(long signatureKeyId) {
- mSecretKeyId = signatureKeyId;
- // update key selection in EncryptActivity
- mKeySelectionListener.onSigningKeySelected(signatureKeyId);
- updateView();
+ mEncryptInterface.setSignatureKey(signatureKeyId);
}
private void setEncryptionKeyIds(long[] encryptionKeyIds) {
- mEncryptionKeyIds = encryptionKeyIds;
- // update key selection in EncryptActivity
- mKeySelectionListener.onEncryptionKeysSelected(encryptionKeyIds);
- updateView();
+ mEncryptInterface.setEncryptionKeys(encryptionKeyIds);
+ }
+
+ private void setEncryptionUserIds(String[] encryptionUserIds) {
+ mEncryptInterface.setEncryptionUsers(encryptionUserIds);
}
/**
@@ -101,25 +101,21 @@ public class EncryptAsymmetricFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false);
- mSelectKeysButton = (Button) view.findViewById(R.id.btn_selectEncryptKeys);
- mSign = (CheckBox) view.findViewById(R.id.sign);
- mMainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mSelectKeysButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- selectPublicKeys();
+ mSign = (Spinner) view.findViewById(R.id.sign);
+ mSign.setAdapter(mSignAdapter);
+ mSign.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ setSignatureKeyId(parent.getAdapter().getItemId(position));
}
- });
- mSign.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- CheckBox checkBox = (CheckBox) v;
- if (checkBox.isChecked()) {
- selectSecretKey();
- } else {
- setSignatureKeyId(Constants.key.none);
- }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ setSignatureKeyId(Constants.key.none);
}
});
+ mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list);
+ mEncryptKeyView.setThreshold(1); // Start working from first character
return view;
}
@@ -135,6 +131,65 @@ public class EncryptAsymmetricFragment extends Fragment {
// preselect keys given by arguments (given by Intent to EncryptActivity)
preselectKeys(signatureKeyId, encryptionKeyIds, mProviderHelper);
+
+ getLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<Cursor>() {
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ // This is called when a new Loader needs to be created. This
+ // sample only has one Loader, so we don't care about the ID.
+ Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
+
+ // These are the rows that we will retrieve.
+ String[] projection = new String[]{
+ KeyRings._ID,
+ KeyRings.MASTER_KEY_ID,
+ KeyRings.KEY_ID,
+ KeyRings.USER_ID,
+ KeyRings.EXPIRY,
+ KeyRings.IS_REVOKED,
+ // can certify info only related to master key
+ KeyRings.CAN_CERTIFY,
+ // has sign may be any subkey
+ KeyRings.HAS_SIGN,
+ KeyRings.HAS_ANY_SECRET,
+ KeyRings.HAS_SECRET
+ };
+
+ String where = KeyRings.HAS_ANY_SECRET + " = 1";
+
+ // 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, where, null, null);
+ /*return new CursorLoader(getActivity(), KeyRings.buildUnifiedKeyRingsUri(),
+ new String[]{KeyRings.USER_ID, KeyRings.KEY_ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET}, SIGN_KEY_SELECTION,
+ null, null);*/
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mSignAdapter.swapCursor(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mSignAdapter.swapCursor(null);
+ }
+ });
+ mEncryptKeyView.setTokenListener(new TokenCompleteTextView.TokenListener() {
+ @Override
+ public void onTokenAdded(Object token) {
+ if (token instanceof EncryptKeyCompletionView.EncryptionKey) {
+ updateEncryptionKeys();
+ }
+ }
+
+ @Override
+ public void onTokenRemoved(Object token) {
+ if (token instanceof EncryptKeyCompletionView.EncryptionKey) {
+ updateEncryptionKeys();
+ }
+ }
+ });
}
/**
@@ -161,117 +216,125 @@ public class EncryptAsymmetricFragment extends Fragment {
}
if (preselectedEncryptionKeyIds != null) {
- Vector<Long> goodIds = new Vector<Long>();
- for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
+ for (long preselectedId : preselectedEncryptionKeyIds) {
try {
- long id = providerHelper.getCachedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
- preselectedEncryptionKeyIds[i])
- ).getMasterKeyId();
- goodIds.add(id);
+ CachedPublicKeyRing ring = providerHelper.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(preselectedId));
+ mEncryptKeyView.addObject(mEncryptKeyView.new EncryptionKey(ring));
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
- if (goodIds.size() > 0) {
- long[] keyIds = new long[goodIds.size()];
- for (int i = 0; i < goodIds.size(); ++i) {
- keyIds[i] = goodIds.get(i);
- }
- setEncryptionKeyIds(keyIds);
- }
+ updateEncryptionKeys();
}
}
- private void updateView() {
- if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
- mSelectKeysButton.setText(getString(R.string.select_keys_button_default));
- } else {
- mSelectKeysButton.setText(getResources().getQuantityString(
- R.plurals.select_keys_button, mEncryptionKeyIds.length,
- mEncryptionKeyIds.length));
+ private void updateEncryptionKeys() {
+ List<Object> objects = mEncryptKeyView.getObjects();
+ List<Long> keyIds = new ArrayList<Long>();
+ List<String> userIds = new ArrayList<String>();
+ for (Object object : objects) {
+ if (object instanceof EncryptKeyCompletionView.EncryptionKey) {
+ keyIds.add(((EncryptKeyCompletionView.EncryptionKey) object).getKeyId());
+ userIds.add(((EncryptKeyCompletionView.EncryptionKey) object).getUserId());
+ }
+ }
+ long[] keyIdsArr = new long[keyIds.size()];
+ Iterator<Long> iterator = keyIds.iterator();
+ for (int i = 0; i < keyIds.size(); i++) {
+ keyIdsArr[i] = iterator.next();
}
+ setEncryptionKeyIds(keyIdsArr);
+ setEncryptionUserIds(userIds.toArray(new String[userIds.size()]));
+ }
- if (mSecretKeyId == Constants.key.none) {
- mSign.setChecked(false);
- mMainUserId.setText("");
- mMainUserIdRest.setText("");
- } else {
- // See if we can get a user_id from a unified query
- try {
- String[] userIdSplit = mProviderHelper.getCachedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingUri(mSecretKeyId)).getSplitPrimaryUserIdWithFallback();
+ private class SelectSignKeyCursorAdapter extends BaseAdapter implements SpinnerAdapter {
+ private CursorAdapter inner;
+ private int mIndexUserId;
+ private int mIndexKeyId;
+ private int mIndexMasterKeyId;
+
+ public SelectSignKeyCursorAdapter() {
+ inner = new CursorAdapter(null, null, 0) {
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getActivity().getLayoutInflater().inflate(R.layout.encrypt_asymmetric_signkey, null);
+ }
- if (userIdSplit[0] != null) {
- mMainUserId.setText(userIdSplit[0]);
- } else {
- mMainUserId.setText(R.string.user_id_no_name);
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId));
+ ((TextView) view.findViewById(android.R.id.title)).setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")"));
+ ((TextView) view.findViewById(android.R.id.text1)).setText(userId[1]);
+ ((TextView) view.findViewById(android.R.id.text2)).setText(PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId)));
}
- if (userIdSplit[1] != null) {
- mMainUserIdRest.setText(userIdSplit[1]);
- } else {
- mMainUserIdRest.setText(getString(R.string.label_key_id) + ": "
- + PgpKeyHelper.convertKeyIdToHex(mSecretKeyId));
+
+ @Override
+ public long getItemId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(mIndexMasterKeyId);
}
- } catch (PgpGeneralException e) {
- Notify.showNotify(getActivity(), "Key not found! This is a bug!", Notify.Style.ERROR);
- }
- mSign.setChecked(true);
+ };
}
- }
- private void selectPublicKeys() {
- Intent intent = new Intent(getActivity(), SelectPublicKeyActivity.class);
- Vector<Long> keyIds = new Vector<Long>();
- if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
- for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
- keyIds.add(mEncryptionKeyIds[i]);
+ public Cursor swapCursor(Cursor newCursor) {
+ if (newCursor == null) return inner.swapCursor(null);
+
+ mIndexKeyId = newCursor.getColumnIndex(KeyRings.KEY_ID);
+ mIndexUserId = newCursor.getColumnIndex(KeyRings.USER_ID);
+ mIndexMasterKeyId = newCursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
+ if (newCursor.moveToFirst()) {
+ do {
+ if (newCursor.getLong(mIndexMasterKeyId) == mEncryptInterface.getSignatureKey()) {
+ mSign.setSelection(newCursor.getPosition() + 1);
+ }
+ } while (newCursor.moveToNext());
}
+ return inner.swapCursor(newCursor);
}
- long[] initialKeyIds = null;
- if (keyIds.size() > 0) {
- initialKeyIds = new long[keyIds.size()];
- for (int i = 0; i < keyIds.size(); ++i) {
- initialKeyIds[i] = keyIds.get(i);
- }
+
+ @Override
+ public int getCount() {
+ return inner.getCount() + 1;
}
- intent.putExtra(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds);
- startActivityForResult(intent, REQUEST_CODE_PUBLIC_KEYS);
- }
- private void selectSecretKey() {
- Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class);
- intent.putExtra(SelectSecretKeyActivity.EXTRA_FILTER_SIGN, true);
- startActivityForResult(intent, REQUEST_CODE_SECRET_KEYS);
- }
+ @Override
+ public Object getItem(int position) {
+ if (position == 0) return null;
+ return inner.getItem(position - 1);
+ }
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_PUBLIC_KEYS: {
- if (resultCode == Activity.RESULT_OK) {
- Bundle bundle = data.getExtras();
- setEncryptionKeyIds(bundle
- .getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS));
- }
- break;
- }
+ @Override
+ public long getItemId(int position) {
+ if (position == 0) return Constants.key.none;
+ return inner.getItemId(position - 1);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = getDropDownView(position, convertView, parent);
+ v.findViewById(android.R.id.text1).setVisibility(View.GONE);
+ return v;
+ }
- case REQUEST_CODE_SECRET_KEYS: {
- if (resultCode == Activity.RESULT_OK) {
- Uri uriMasterKey = data.getData();
- setSignatureKeyId(Long.valueOf(uriMasterKey.getLastPathSegment()));
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (position == 0) {
+ if (convertView == null) {
+ v = inner.newView(null, null, parent);
} else {
- setSignatureKeyId(Constants.key.none);
+ v = convertView;
}
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
-
- break;
+ ((TextView) v.findViewById(android.R.id.title)).setText("None");
+ v.findViewById(android.R.id.text1).setVisibility(View.GONE);
+ v.findViewById(android.R.id.text2).setVisibility(View.GONE);
+ } else {
+ v = inner.getView(position - 1, convertView, parent);
+ v.findViewById(android.R.id.text1).setVisibility(View.VISIBLE);
+ v.findViewById(android.R.id.text2).setVisibility(View.VISIBLE);
}
+ return v;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
index 345e38a0e..8a9e17020 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
@@ -17,66 +17,50 @@
package org.sufficientlysecure.keychain.ui;
+import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
-import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Point;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.provider.OpenableColumns;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.Spinner;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
-import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
-import org.sufficientlysecure.keychain.util.Choice;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.Notify;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
-public class EncryptFileFragment extends Fragment {
- public static final String ARG_FILENAME = "filename";
- public static final String ARG_ASCII_ARMOR = "ascii_armor";
+public class EncryptFileFragment extends Fragment implements EncryptActivityInterface.UpdateListener {
+ public static final String ARG_URIS = "uris";
- private static final int REQUEST_CODE_FILE = 0x00007003;
+ private static final int REQUEST_CODE_INPUT = 0x00007003;
+ private static final int REQUEST_CODE_OUTPUT = 0x00007007;
private EncryptActivityInterface mEncryptInterface;
// view
- private CheckBox mAsciiArmor = null;
- private Spinner mFileCompression = null;
- private EditText mFilename = null;
- private CheckBox mDeleteAfter = null;
- private CheckBox mShareAfter = null;
- private ImageButton mBrowse = null;
+ private View mAddView;
+ private View mShareFile;
private View mEncryptFile;
-
- private FileDialogFragment mFileDialog;
-
- // model
- private String mInputFilename = null;
- private Uri mInputUri = null;
- private String mOutputFilename = null;
- private Uri mOutputUri = null;
+ private ListView mSelectedFiles;
+ private SelectedFilesAdapter mAdapter = new SelectedFilesAdapter();
+ private final Map<Uri, Bitmap> thumbnailCache = new HashMap<Uri, Bitmap>();
@Override
public void onAttach(Activity activity) {
@@ -99,52 +83,27 @@ public class EncryptFileFragment extends Fragment {
mEncryptFile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- encryptClicked();
+ encryptClicked(false);
}
});
-
- mFilename = (EditText) view.findViewById(R.id.filename);
- mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
- mBrowse.setOnClickListener(new View.OnClickListener() {
+ mShareFile = view.findViewById(R.id.action_encrypt_share);
+ mShareFile.setOnClickListener(new View.OnClickListener() {
+ @Override
public void onClick(View v) {
- if (Constants.KITKAT) {
- FileHelper.openDocument(EncryptFileFragment.this, mInputUri, "*/*", REQUEST_CODE_FILE);
- } else {
- FileHelper.openFile(EncryptFileFragment.this, mFilename.getText().toString(), "*/*",
- REQUEST_CODE_FILE);
- }
+ encryptClicked(true);
}
});
- mFileCompression = (Spinner) view.findViewById(R.id.fileCompression);
- Choice[] choices = new Choice[]{
- new Choice(Constants.choice.compression.none, getString(R.string.choice_none) + " ("
- + getString(R.string.compression_fast) + ")"),
- new Choice(Constants.choice.compression.zip, "ZIP ("
- + getString(R.string.compression_fast) + ")"),
- new Choice(Constants.choice.compression.zlib, "ZLIB ("
- + getString(R.string.compression_fast) + ")"),
- new Choice(Constants.choice.compression.bzip2, "BZIP2 ("
- + getString(R.string.compression_very_slow) + ")"),
- };
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getActivity(),
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mFileCompression.setAdapter(adapter);
-
- int defaultFileCompression = Preferences.getPreferences(getActivity()).getDefaultFileCompression();
- for (int i = 0; i < choices.length; ++i) {
- if (choices[i].getId() == defaultFileCompression) {
- mFileCompression.setSelection(i);
- break;
+ mAddView = inflater.inflate(R.layout.file_list_entry_add, null);
+ mAddView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ addInputUri();
}
- }
-
- mDeleteAfter = (CheckBox) view.findViewById(R.id.deleteAfterEncryption);
- mShareAfter = (CheckBox) view.findViewById(R.id.shareAfterEncryption);
-
- mAsciiArmor = (CheckBox) view.findViewById(R.id.asciiArmor);
- mAsciiArmor.setChecked(Preferences.getPreferences(getActivity()).getDefaultAsciiArmor());
+ });
+ mSelectedFiles = (ListView) view.findViewById(R.id.selected_files_list);
+ mSelectedFiles.addFooterView(mAddView);
+ mSelectedFiles.setAdapter(mAdapter);
return view;
}
@@ -153,267 +112,128 @@ public class EncryptFileFragment extends Fragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- String filename = getArguments().getString(ARG_FILENAME);
- if (filename != null) {
- mFilename.setText(filename);
- }
- boolean asciiArmor = getArguments().getBoolean(ARG_ASCII_ARMOR);
- if (asciiArmor) {
- mAsciiArmor.setChecked(asciiArmor);
- }
+ addInputUris(getArguments().<Uri>getParcelableArrayList(ARG_URIS));
}
- /**
- * Guess output filename based on input path
- *
- * @param path
- * @return Suggestion for output filename
- */
- private String guessOutputFilename(String path) {
- // output in the same directory but with additional ending
- File file = new File(path);
- String ending = (mAsciiArmor.isChecked() ? ".asc" : ".gpg");
- String outputFilename = file.getParent() + File.separator + file.getName() + ending;
-
- return outputFilename;
+ private void addInputUri() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ FileHelper.openDocument(EncryptFileFragment.this, "*/*", true, REQUEST_CODE_INPUT);
+ } else {
+ FileHelper.openFile(EncryptFileFragment.this, mEncryptInterface.getInputUris().isEmpty() ?
+ null : mEncryptInterface.getInputUris().get(mEncryptInterface.getInputUris().size() - 1),
+ "*/*", REQUEST_CODE_INPUT);
+ }
}
- private void showOutputFileDialog() {
- // Message is received after file is selected
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == FileDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- if (data.containsKey(FileDialogFragment.MESSAGE_DATA_URI)) {
- mOutputUri = data.getParcelable(FileDialogFragment.MESSAGE_DATA_URI);
- } else {
- mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
- }
- encryptStart();
- }
+ private void addInputUris(List<Uri> uris) {
+ if (uris != null) {
+ for (Uri uri : uris) {
+ addInputUri(uri);
}
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- mFileDialog = FileDialogFragment.newInstance(messenger,
- getString(R.string.title_encrypt_to_file),
- getString(R.string.specify_file_to_encrypt_to), mOutputFilename, null);
-
- mFileDialog.show(getActivity().getSupportFragmentManager(), "fileDialog");
- }
-
- private void encryptClicked() {
- String currentFilename = mFilename.getText().toString();
- if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
- mInputUri = null;
- mInputFilename = mFilename.getText().toString();
- }
-
- if (mInputUri == null) {
- mOutputFilename = guessOutputFilename(mInputFilename);
}
+ }
- if (mInputFilename.equals("")) {
- Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
+ private void addInputUri(Uri inputUri) {
+ if (inputUri == null) {
return;
}
- if (mInputUri == null && !mInputFilename.startsWith("content")) {
- File file = new File(mInputFilename);
- if (!file.exists() || !file.isFile()) {
- Notify.showNotify(
- getActivity(),
- getString(R.string.error_message,
- getString(R.string.error_file_not_found)), Notify.Style.ERROR
- );
- return;
- }
- }
+ mEncryptInterface.getInputUris().add(inputUri);
+ mEncryptInterface.notifyUpdate();
+ mSelectedFiles.requestFocus();
- if (mEncryptInterface.isModeSymmetric()) {
- // symmetric encryption
+ /**
+ * We hide the encrypt to file button if multiple files are selected.
+ *
+ * With Android L it will be possible to select a target directory for multiple files, so we might want to
+ * change this later
+ */
- boolean gotPassphrase = (mEncryptInterface.getPassphrase() != null
- && mEncryptInterface.getPassphrase().length() != 0);
- if (!gotPassphrase) {
- Notify.showNotify(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
- ;
- return;
- }
-
- if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
- Notify.showNotify(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR);
- return;
- }
+ if (mEncryptInterface.getInputUris().size() > 1) {
+ mEncryptFile.setVisibility(View.GONE);
} else {
- // asymmetric encryption
-
- boolean gotEncryptionKeys = (mEncryptInterface.getEncryptionKeys() != null
- && mEncryptInterface.getEncryptionKeys().length > 0);
-
- if (!gotEncryptionKeys) {
- Notify.showNotify(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR);
- return;
- }
-
- if (!gotEncryptionKeys && mEncryptInterface.getSignatureKey() == 0) {
- Notify.showNotify(getActivity(), R.string.select_encryption_or_signature_key,
- Notify.Style.ERROR);
- return;
- }
-
- if (mEncryptInterface.getSignatureKey() != 0 &&
- PassphraseCacheService.getCachedPassphrase(getActivity(),
- mEncryptInterface.getSignatureKey()) == null) {
- PassphraseDialogFragment.show(getActivity(), mEncryptInterface.getSignatureKey(),
- new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- showOutputFileDialog();
- }
- }
- });
-
- return;
- }
+ mEncryptFile.setVisibility(View.VISIBLE);
}
-
- showOutputFileDialog();
}
- private void encryptStart() {
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
+ private void delInputUri(int position) {
+ mEncryptInterface.getInputUris().remove(position);
+ mEncryptInterface.notifyUpdate();
+ mSelectedFiles.requestFocus();
- // fill values for this action
- Bundle data = new Bundle();
-
- Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
- + mOutputFilename + ",mInputUri=" + mInputUri + ", mOutputUri="
- + mOutputUri);
-
- if (mInputUri != null) {
- data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI);
- data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri);
+ if (mEncryptInterface.getInputUris().size() > 1) {
+ mEncryptFile.setVisibility(View.GONE);
} else {
- data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_FILE);
- data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
+ mEncryptFile.setVisibility(View.VISIBLE);
}
+ }
- if (mOutputUri != null) {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI);
- data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri);
+ private void showOutputFileDialog() {
+ if (mEncryptInterface.getInputUris().size() > 1 || mEncryptInterface.getInputUris().isEmpty()) {
+ throw new IllegalStateException();
+ }
+ Uri inputUri = mEncryptInterface.getInputUris().get(0);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ File file = new File(inputUri.getPath());
+ File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
+ String targetName = FileHelper.getFilename(getActivity(), inputUri) +
+ (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg");
+ File targetFile = new File(parentDir, targetName);
+ FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file),
+ getString(R.string.specify_file_to_encrypt_to), targetFile, REQUEST_CODE_OUTPUT);
} else {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_FILE);
- data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
+ FileHelper.saveDocument(this, "*/*", FileHelper.getFilename(getActivity(), inputUri) +
+ (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"), REQUEST_CODE_OUTPUT);
}
+ }
- if (mEncryptInterface.isModeSymmetric()) {
- Log.d(Constants.TAG, "Symmetric encryption enabled!");
- String passphrase = mEncryptInterface.getPassphrase();
- if (passphrase.length() == 0) {
- passphrase = null;
+ private void encryptClicked(boolean share) {
+ if (share) {
+ mEncryptInterface.getOutputUris().clear();
+ for (Uri uri : mEncryptInterface.getInputUris()) {
+ String targetName = FileHelper.getFilename(getActivity(), uri) +
+ (mEncryptInterface.isUseArmor() ? ".asc" : ".gpg");
+ mEncryptInterface.getOutputUris().add(TemporaryStorageProvider.createFile(getActivity(), targetName));
}
- data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
- } else {
- data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_ID,
- mEncryptInterface.getSignatureKey());
- data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS,
- mEncryptInterface.getEncryptionKeys());
+ mEncryptInterface.startEncrypt(true);
+ } else if (mEncryptInterface.getInputUris().size() == 1) {
+ showOutputFileDialog();
}
+ }
- boolean useAsciiArmor = mAsciiArmor.isChecked();
- data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, useAsciiArmor);
-
- int compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
- data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
- getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- Notify.showNotify(getActivity(), R.string.encrypt_sign_successful,
- Notify.Style.INFO);
-
- if (mDeleteAfter.isChecked()) {
- // Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog;
- if (mInputUri != null) {
- deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);
- } else {
- deleteFileDialog = DeleteFileDialogFragment
- .newInstance(mInputFilename);
- }
- deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
- }
-
- if (mShareAfter.isChecked()) {
- // Share encrypted file
- Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
- sendFileIntent.setType("*/*");
- if (mOutputUri != null) {
- sendFileIntent.putExtra(Intent.EXTRA_STREAM, mOutputUri);
- } else {
- sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
- }
- startActivity(Intent.createChooser(sendFileIntent,
- getString(R.string.title_share_file)));
- }
- }
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ public boolean handleClipData(Intent data) {
+ if (data.getClipData() != null && data.getClipData().getItemCount() > 0) {
+ for (int i = 0; i < data.getClipData().getItemCount(); i++) {
+ Uri uri = data.getClipData().getItemAt(i).getUri();
+ if (uri != null) addInputUri(uri);
}
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
+ return true;
+ }
+ return false;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
- case REQUEST_CODE_FILE: {
+ case REQUEST_CODE_INPUT: {
if (resultCode == Activity.RESULT_OK && data != null) {
- if (Constants.KITKAT) {
- mInputUri = data.getData();
- Cursor cursor = getActivity().getContentResolver().query(mInputUri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
- if (cursor != null) {
- if (cursor.moveToNext()) {
- mInputFilename = cursor.getString(0);
- mFilename.setText(mInputFilename);
- }
- cursor.close();
- }
- } else {
- try {
- String path = FileHelper.getPath(getActivity(), data.getData());
- Log.d(Constants.TAG, "path=" + path);
-
- mFilename.setText(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!");
- }
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || !handleClipData(data)) {
+ addInputUri(data.getData());
}
}
return;
}
+ case REQUEST_CODE_OUTPUT: {
+ // This happens after output file was selected, so start our operation
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ mEncryptInterface.getOutputUris().clear();
+ mEncryptInterface.getOutputUris().add(data.getData());
+ mEncryptInterface.notifyUpdate();
+ mEncryptInterface.startEncrypt(false);
+ }
+ return;
+ }
default: {
super.onActivityResult(requestCode, resultCode, data);
@@ -422,4 +242,68 @@ public class EncryptFileFragment extends Fragment {
}
}
}
+
+ @Override
+ public void onNotifyUpdate() {
+ // Clear cache if needed
+ for (Uri uri : new HashSet<Uri>(thumbnailCache.keySet())) {
+ if (!mEncryptInterface.getInputUris().contains(uri)) {
+ thumbnailCache.remove(uri);
+ }
+ }
+
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private class SelectedFilesAdapter extends BaseAdapter {
+ @Override
+ public int getCount() {
+ return mEncryptInterface.getInputUris().size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mEncryptInterface.getInputUris().get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getItem(position).hashCode();
+ }
+
+ @Override
+ public View getView(final int position, View convertView, ViewGroup parent) {
+ Uri inputUri = mEncryptInterface.getInputUris().get(position);
+ View view;
+ if (convertView == null) {
+ view = getActivity().getLayoutInflater().inflate(R.layout.file_list_entry, null);
+ } else {
+ view = convertView;
+ }
+ ((TextView) view.findViewById(R.id.filename)).setText(FileHelper.getFilename(getActivity(), inputUri));
+ long size = FileHelper.getFileSize(getActivity(), inputUri);
+ if (size == -1) {
+ ((TextView) view.findViewById(R.id.filesize)).setText("");
+ } else {
+ ((TextView) view.findViewById(R.id.filesize)).setText(FileHelper.readableFileSize(size));
+ }
+ view.findViewById(R.id.action_remove_file_from_list).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ delInputUri(position);
+ }
+ });
+ int px = OtherHelper.dpToPx(getActivity(), 48);
+ if (!thumbnailCache.containsKey(inputUri)) {
+ thumbnailCache.put(inputUri, FileHelper.getThumbnail(getActivity(), inputUri, new Point(px, px)));
+ }
+ Bitmap bitmap = thumbnailCache.get(inputUri);
+ if (bitmap != null) {
+ ((ImageView) view.findViewById(R.id.thumbnail)).setImageBitmap(bitmap);
+ } else {
+ ((ImageView) view.findViewById(R.id.thumbnail)).setImageResource(R.drawable.ic_doc_generic_am);
+ }
+ return view;
+ }
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
index e1760b4ed..6d753088b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
@@ -18,28 +18,16 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.Intent;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.Notify;
public class EncryptMessageFragment extends Fragment {
public static final String ARG_TEXT = "text";
@@ -69,18 +57,34 @@ public class EncryptMessageFragment extends Fragment {
View view = inflater.inflate(R.layout.encrypt_message_fragment, container, false);
mMessage = (TextView) view.findViewById(R.id.message);
+ mMessage.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ mEncryptInterface.setMessage(s.toString());
+ }
+ });
mEncryptClipboard = view.findViewById(R.id.action_encrypt_clipboard);
mEncryptShare = view.findViewById(R.id.action_encrypt_share);
mEncryptClipboard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- encryptClicked(true);
+ mEncryptInterface.startEncrypt(false);
}
});
mEncryptShare.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- encryptClicked(false);
+ mEncryptInterface.startEncrypt(true);
}
});
@@ -92,7 +96,7 @@ public class EncryptMessageFragment extends Fragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- String text = getArguments().getString(ARG_TEXT);
+ String text = mEncryptInterface.getMessage();
if (text != null) {
mMessage.setText(text);
}
@@ -117,138 +121,4 @@ public class EncryptMessageFragment extends Fragment {
return message;
}
-
- private void encryptClicked(final boolean toClipboard) {
- if (mEncryptInterface.isModeSymmetric()) {
- // symmetric encryption
-
- boolean gotPassphrase = (mEncryptInterface.getPassphrase() != null
- && mEncryptInterface.getPassphrase().length() != 0);
- if (!gotPassphrase) {
- Notify.showNotify(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR);
- return;
- }
-
- if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
- Notify.showNotify(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR);
- return;
- }
-
- } else {
- // asymmetric encryption
-
- boolean gotEncryptionKeys = (mEncryptInterface.getEncryptionKeys() != null
- && mEncryptInterface.getEncryptionKeys().length > 0);
-
- if (!gotEncryptionKeys && mEncryptInterface.getSignatureKey() == 0) {
- Notify.showNotify(getActivity(), R.string.select_encryption_or_signature_key,
- Notify.Style.ERROR);
- return;
- }
-
- if (mEncryptInterface.getSignatureKey() != 0 &&
- PassphraseCacheService.getCachedPassphrase(getActivity(),
- mEncryptInterface.getSignatureKey()) == null) {
- PassphraseDialogFragment.show(getActivity(), mEncryptInterface.getSignatureKey(),
- new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- encryptStart(toClipboard);
- }
- }
- });
-
- return;
- }
- }
-
- encryptStart(toClipboard);
- }
-
- private void encryptStart(final boolean toClipboard) {
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES);
-
- String message = mMessage.getText().toString();
-
- if (mEncryptInterface.isModeSymmetric()) {
- Log.d(Constants.TAG, "Symmetric encryption enabled!");
- String passphrase = mEncryptInterface.getPassphrase();
- if (passphrase.length() == 0) {
- passphrase = null;
- }
- data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
- } else {
- data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_ID,
- mEncryptInterface.getSignatureKey());
- data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS,
- mEncryptInterface.getEncryptionKeys());
-
- boolean signOnly = (mEncryptInterface.getEncryptionKeys() == null
- || mEncryptInterface.getEncryptionKeys().length == 0);
- if (signOnly) {
- message = fixBadCharactersForGmail(message);
- }
- }
-
- data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, message.getBytes());
-
- data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true);
-
- int compressionId = Preferences.getPreferences(getActivity()).getDefaultMessageCompression();
- data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
- getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get returned data bundle
- Bundle data = message.getData();
-
- String output = new String(data.getByteArray(KeychainIntentService.RESULT_BYTES));
- Log.d(Constants.TAG, "output: " + output);
-
- if (toClipboard) {
- ClipboardReflection.copyToClipboard(getActivity(), output);
- Notify.showNotify(getActivity(),
- R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
- } else {
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
-
- // Type is set to text/plain so that encrypted messages can
- // be sent with Whatsapp, Hangouts, SMS etc...
- sendIntent.setType("text/plain");
-
- sendIntent.putExtra(Intent.EXTRA_TEXT, output);
- startActivity(Intent.createChooser(sendIntent,
- getString(R.string.title_share_with)));
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
- }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java
index 8efa07953..86731b162 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java
@@ -29,27 +29,20 @@ import android.widget.EditText;
import org.sufficientlysecure.keychain.R;
-public class EncryptSymmetricFragment extends Fragment {
+public class EncryptSymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener {
- OnSymmetricKeySelection mPassphraseUpdateListener;
+ EncryptActivityInterface mEncryptInterface;
private EditText mPassphrase;
private EditText mPassphraseAgain;
- // Container Activity must implement this interface
- public interface OnSymmetricKeySelection {
- public void onPassphraseUpdate(String passphrase);
-
- public void onPassphraseAgainUpdate(String passphrase);
- }
-
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
- mPassphraseUpdateListener = (OnSymmetricKeySelection) activity;
+ mEncryptInterface = (EncryptActivityInterface) activity;
} catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement OnSymmetricKeySelection");
+ throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface");
}
}
@@ -62,7 +55,7 @@ public class EncryptSymmetricFragment extends Fragment {
mPassphrase = (EditText) view.findViewById(R.id.passphrase);
mPassphraseAgain = (EditText) view.findViewById(R.id.passphraseAgain);
- mPassphrase.addTextChangedListener(new TextWatcher() {
+ TextWatcher textWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@@ -74,25 +67,21 @@ public class EncryptSymmetricFragment extends Fragment {
@Override
public void afterTextChanged(Editable s) {
// update passphrase in EncryptActivity
- mPassphraseUpdateListener.onPassphraseUpdate(s.toString());
- }
- });
- mPassphraseAgain.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ if (mPassphrase.getText().toString().equals(mPassphraseAgain.getText().toString())) {
+ mEncryptInterface.setPassphrase(s.toString());
+ } else {
+ mEncryptInterface.setPassphrase(null);
+ }
}
+ };
+ mPassphrase.addTextChangedListener(textWatcher);
+ mPassphraseAgain.addTextChangedListener(textWatcher);
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
+ return view;
+ }
- @Override
- public void afterTextChanged(Editable s) {
- // update passphrase in EncryptActivity
- mPassphraseUpdateListener.onPassphraseAgainUpdate(s.toString());
- }
- });
+ @Override
+ public void onNotifyUpdate() {
- return view;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 4a606a1b3..255290de3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -40,7 +40,6 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.util.FileImportCache;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@@ -49,6 +48,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
+import org.sufficientlysecure.keychain.util.FileImportCache;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
index ce885c419..cb53647f6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
@@ -66,7 +66,7 @@ public class ImportKeysFileFragment extends Fragment {
// open .asc or .gpg files
// setting it to text/plain prevents Cyanogenmod's file manager from selecting asc
// or gpg types!
- FileHelper.openFile(ImportKeysFileFragment.this, Constants.Path.APP_DIR + "/",
+ FileHelper.openFile(ImportKeysFileFragment.this, Uri.fromFile(Constants.Path.APP_DIR),
"*/*", REQUEST_CODE_FILE);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index 50ff5c753..7a6e78a7d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -94,22 +94,22 @@ public class KeyListActivity extends DrawerActivity {
case R.id.menu_key_list_debug_read:
try {
- KeychainDatabase.debugRead(this);
- Notify.showNotify(this, "Restored Notify.Style backup", Notify.Style.INFO);
+ KeychainDatabase.debugBackup(this, true);
+ Notify.showNotify(this, "Restored debug_backup.db", Notify.Style.INFO);
getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null);
} catch (IOException e) {
Log.e(Constants.TAG, "IO Error", e);
- Notify.showNotify(this, "IO Notify.Style: " + e.getMessage(), Notify.Style.ERROR);
+ Notify.showNotify(this, "IO Error " + e.getMessage(), Notify.Style.ERROR);
}
return true;
case R.id.menu_key_list_debug_write:
try {
- KeychainDatabase.debugWrite(this);
- Notify.showNotify(this, "Backup Notify.Style", Notify.Style.INFO);
+ KeychainDatabase.debugBackup(this, false);
+ Notify.showNotify(this, "Backup to debug_backup.db completed", Notify.Style.INFO);
} catch(IOException e) {
Log.e(Constants.TAG, "IO Error", e);
- Notify.showNotify(this, "IO Notify.Style: " + e.getMessage(), Notify.Style.ERROR);
+ Notify.showNotify(this, "IO Error: " + e.getMessage(), Notify.Style.ERROR);
}
return true;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index aa17aea3d..3c97b1128 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -47,10 +47,10 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
-import android.widget.Button;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -176,8 +176,8 @@ public class KeyListFragment extends LoaderFragment
case R.id.menu_key_list_multi_export: {
ids = mAdapter.getCurrentSelectedMasterKeyIds();
ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
- mExportHelper.showExportKeysDialog(
- ids, Constants.Path.APP_DIR_FILE, mAdapter.isAnySecretSelected());
+ mExportHelper.showExportKeysDialog(ids, Constants.Path.APP_DIR_FILE,
+ mAdapter.isAnySecretSelected());
break;
}
case R.id.menu_key_list_multi_select_all: {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
index 43de6774b..0e948bf7f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
@@ -41,9 +41,7 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogEntryPar
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.util.Log;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
public class LogDisplayFragment extends ListFragment implements OnTouchListener {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
index 283b79b13..a6561dfad 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -27,6 +27,7 @@ import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
@@ -88,10 +89,10 @@ public class PreferencesActivity extends PreferenceActivity {
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM));
int[] valueIds = new int[]{
- Constants.choice.compression.none,
- Constants.choice.compression.zip,
- Constants.choice.compression.zlib,
- Constants.choice.compression.bzip2,
+ CompressionAlgorithmTags.UNCOMPRESSED,
+ CompressionAlgorithmTags.ZIP,
+ CompressionAlgorithmTags.ZLIB,
+ CompressionAlgorithmTags.BZIP2,
};
String[] entries = new String[]{
getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
@@ -229,10 +230,10 @@ public class PreferencesActivity extends PreferenceActivity {
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM));
int[] valueIds = new int[]{
- Constants.choice.compression.none,
- Constants.choice.compression.zip,
- Constants.choice.compression.zlib,
- Constants.choice.compression.bzip2,
+ CompressionAlgorithmTags.UNCOMPRESSED,
+ CompressionAlgorithmTags.ZIP,
+ CompressionAlgorithmTags.ZLIB,
+ CompressionAlgorithmTags.BZIP2,
};
String[] entries = new String[]{
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index 5201b5df8..341e11d1d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -34,8 +34,8 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 44a51a75f..28f7b8bf5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -42,7 +42,6 @@ import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -57,9 +56,9 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
+import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout.TabColorizer;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
import org.sufficientlysecure.keychain.util.Notify;
import java.util.Date;
@@ -103,7 +102,6 @@ public class ViewKeyActivity extends ActionBarActivity implements
@Override
protected void onCreate(Bundle savedInstanceState) {
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState);
mExportHelper = new ExportHelper(this);
@@ -296,8 +294,7 @@ public class ViewKeyActivity extends ActionBarActivity implements
exportHelper.showExportKeysDialog(
new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)},
- Constants.Path.APP_DIR_FILE,
- ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) == 1)
+ Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) == 1)
);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
index e98562690..5a55b0dad 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
@@ -150,7 +150,7 @@ public class ViewKeyCertsFragment extends LoaderFragment
Intent viewIntent = new Intent(getActivity(), ViewCertActivity.class);
viewIntent.setData(Certs.buildCertsSpecificUri(
- Long.toString(masterKeyId), Long.toString(rank), Long.toString(certifierId)));
+ masterKeyId, rank, certifierId));
startActivity(viewIntent);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
index 54ab76464..ae0bea5e9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
@@ -49,7 +49,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index 7a55f9aaa..1f809cc51 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -111,7 +111,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
convertView = mInflater.inflate(R.layout.import_keys_list_entry, null);
holder.mainUserId = (TextView) convertView.findViewById(R.id.mainUserId);
holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest);
- holder.keyId = (TextView) convertView.findViewById(R.id.keyId);
+ holder.keyId = (TextView) convertView.findViewById(R.id.subkey_item_key_id);
holder.fingerprint = (TextView) convertView.findViewById(R.id.view_key_fingerprint);
holder.algorithm = (TextView) convertView.findViewById(R.id.algorithm);
holder.status = (TextView) convertView.findViewById(R.id.status);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
index 4971c535c..04947da93 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -33,7 +33,6 @@ import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
index 3e3098b10..330254a8f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
@@ -20,13 +20,8 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBarActivity;
-import android.view.ViewGroup;
-
-import org.sufficientlysecure.keychain.Constants;
import java.util.ArrayList;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
index e69a63c63..0e2177568 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
@@ -152,7 +152,7 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
holder.view = view;
holder.mainUserId = (TextView) view.findViewById(R.id.mainUserId);
holder.mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- holder.keyId = (TextView) view.findViewById(R.id.keyId);
+ holder.keyId = (TextView) view.findViewById(R.id.subkey_item_key_id);
holder.status = (TextView) view.findViewById(R.id.status);
holder.selected = (CheckBox) view.findViewById(R.id.selected);
view.setTag(holder);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
index c2a882fdb..d457e75bd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.content.res.ColorStateList;
import android.database.Cursor;
+import android.graphics.Typeface;
import android.support.v4.widget.CursorAdapter;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
@@ -89,6 +90,20 @@ public class SubkeysAdapter extends CursorAdapter {
return mCursor.getLong(INDEX_KEY_ID);
}
+ public long getCreationDate(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(INDEX_CREATION);
+ }
+
+ public Long getExpiryDate(int position) {
+ mCursor.moveToPosition(position);
+ if (mCursor.isNull(INDEX_EXPIRY)) {
+ return null;
+ } else {
+ return mCursor.getLong(INDEX_EXPIRY);
+ }
+ }
+
@Override
public Cursor swapCursor(Cursor newCursor) {
hasAnySecret = false;
@@ -106,15 +121,18 @@ public class SubkeysAdapter extends CursorAdapter {
@Override
public void bindView(View view, Context context, Cursor cursor) {
- TextView vKeyId = (TextView) view.findViewById(R.id.keyId);
- TextView vKeyDetails = (TextView) view.findViewById(R.id.keyDetails);
- TextView vKeyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
- ImageView vMasterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
- ImageView vCertifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
- ImageView vEncryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
- ImageView vSignIcon = (ImageView) view.findViewById(R.id.ic_signKey);
- ImageView vRevokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
- ImageView vEditImage = (ImageView) view.findViewById(R.id.user_id_item_edit_image);
+ TextView vKeyId = (TextView) view.findViewById(R.id.subkey_item_key_id);
+ TextView vKeyDetails = (TextView) view.findViewById(R.id.subkey_item_details);
+ TextView vKeyExpiry = (TextView) view.findViewById(R.id.subkey_item_expiry);
+ ImageView vCertifyIcon = (ImageView) view.findViewById(R.id.subkey_item_ic_certify);
+ ImageView vEncryptIcon = (ImageView) view.findViewById(R.id.subkey_item_ic_encrypt);
+ ImageView vSignIcon = (ImageView) view.findViewById(R.id.subkey_item_ic_sign);
+ ImageView vRevokedIcon = (ImageView) view.findViewById(R.id.subkey_item_ic_revoked);
+ ImageView vEditImage = (ImageView) view.findViewById(R.id.subkey_item_edit_image);
+
+ // not used:
+ ImageView deleteImage = (ImageView) view.findViewById(R.id.subkey_item_delete_button);
+ deleteImage.setVisibility(View.GONE);
long keyId = cursor.getLong(INDEX_KEY_ID);
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
@@ -133,14 +151,26 @@ public class SubkeysAdapter extends CursorAdapter {
vKeyDetails.setText(algorithmStr);
}
+ boolean isMasterKey = cursor.getInt(INDEX_RANK) == 0;
+ if (isMasterKey) {
+ vKeyId.setTypeface(null, Typeface.BOLD);
+ } else {
+ vKeyId.setTypeface(null, Typeface.NORMAL);
+ }
+
// Set icons according to properties
- vMasterKeyIcon.setVisibility(cursor.getInt(INDEX_RANK) == 0 ? View.VISIBLE : View.INVISIBLE);
vCertifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE);
vEncryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE);
vSignIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE);
+ // TODO: missing icon for authenticate
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
+ Date expiryDate = null;
+ if (!cursor.isNull(INDEX_EXPIRY)) {
+ expiryDate = new Date(cursor.getLong(INDEX_EXPIRY) * 1000);
+ }
+
// for edit key
if (mSaveKeyringParcel != null) {
boolean revokeThisSubkey = (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId));
@@ -151,24 +181,22 @@ public class SubkeysAdapter extends CursorAdapter {
}
}
+ SaveKeyringParcel.SubkeyChange subkeyChange = mSaveKeyringParcel.getSubkeyChange(keyId);
+ if (subkeyChange != null) {
+ if (subkeyChange.mExpiry == null) {
+ expiryDate = null;
+ } else {
+ expiryDate = new Date(subkeyChange.mExpiry * 1000);
+ }
+ }
+
vEditImage.setVisibility(View.VISIBLE);
} else {
vEditImage.setVisibility(View.GONE);
}
- if (isRevoked) {
- vRevokedKeyIcon.setVisibility(View.VISIBLE);
- } else {
- vKeyId.setTextColor(mDefaultTextColor);
- vKeyDetails.setTextColor(mDefaultTextColor);
- vKeyExpiry.setTextColor(mDefaultTextColor);
-
- vRevokedKeyIcon.setVisibility(View.GONE);
- }
-
boolean isExpired;
- if (!cursor.isNull(INDEX_EXPIRY)) {
- Date expiryDate = new Date(cursor.getLong(INDEX_EXPIRY) * 1000);
+ if (expiryDate != null) {
isExpired = expiryDate.before(new Date());
vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": "
@@ -179,6 +207,16 @@ public class SubkeysAdapter extends CursorAdapter {
vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": " + context.getString(R.string.none));
}
+ if (isRevoked) {
+ vRevokedIcon.setVisibility(View.VISIBLE);
+ } else {
+ vKeyId.setTextColor(mDefaultTextColor);
+ vKeyDetails.setTextColor(mDefaultTextColor);
+ vKeyExpiry.setTextColor(mDefaultTextColor);
+
+ vRevokedIcon.setVisibility(View.GONE);
+ }
+
// if key is expired or revoked, strike through text
boolean isInvalid = isRevoked || isExpired;
if (isInvalid) {
@@ -195,7 +233,7 @@ public class SubkeysAdapter extends CursorAdapter {
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.view_key_subkey_item, null);
if (mDefaultTextColor == null) {
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ TextView keyId = (TextView) view.findViewById(R.id.subkey_item_key_id);
mDefaultTextColor = keyId.getTextColors();
}
return view;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
index 25509fee5..be2e17c63 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
@@ -17,48 +17,29 @@
package org.sufficientlysecure.keychain.ui.adapter;
-import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Build;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Patterns;
+import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView;
import android.widget.ArrayAdapter;
-import android.widget.AutoCompleteTextView;
-import android.widget.EditText;
import android.widget.ImageButton;
-import android.widget.Spinner;
+import android.widget.ImageView;
import android.widget.TextView;
-import org.sufficientlysecure.keychain.Constants;
+import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ContactHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-import org.sufficientlysecure.keychain.ui.dialog.CreateKeyDialogFragment;
-import org.sufficientlysecure.keychain.util.Choice;
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Date;
import java.util.List;
-import java.util.regex.Matcher;
public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAdd> {
private LayoutInflater mInflater;
private Activity mActivity;
- public interface OnAlgorithmSelectedListener {
- public void onAlgorithmSelected(Choice algorithmChoice, int keySize);
- }
-
// hold a private reference to the underlying data List
private List<SaveKeyringParcel.SubkeyAdd> mData;
@@ -70,12 +51,12 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
}
static class ViewHolder {
- public OnAlgorithmSelectedListener mAlgorithmSelectedListener;
- public Spinner mAlgorithmSpinner;
- public Spinner mKeySizeSpinner;
- public TextView mCustomKeyTextView;
- public EditText mCustomKeyEditText;
- public TextView mCustomKeyInfoTextView;
+ public TextView vKeyId;
+ public TextView vKeyDetails;
+ public TextView vKeyExpiry;
+ public ImageView vCertifyIcon;
+ public ImageView vEncryptIcon;
+ public ImageView vSignIcon;
public ImageButton vDelete;
// also hold a reference to the model item
public SaveKeyringParcel.SubkeyAdd mModel;
@@ -84,43 +65,24 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// Not recycled, inflate a new view
- convertView = mInflater.inflate(R.layout.edit_key_subkey_added_item, null);
+ convertView = mInflater.inflate(R.layout.view_key_subkey_item, null);
final ViewHolder holder = new ViewHolder();
- holder.mAlgorithmSpinner = (Spinner) convertView.findViewById(R.id.create_key_algorithm);
- holder.mKeySizeSpinner = (Spinner) convertView.findViewById(R.id.create_key_size);
- holder.mCustomKeyTextView = (TextView) convertView.findViewById(R.id.custom_key_size_label);
- holder.mCustomKeyEditText = (EditText) convertView.findViewById(R.id.custom_key_size_input);
- holder.mCustomKeyInfoTextView = (TextView) convertView.findViewById(R.id.custom_key_size_info);
- holder.vDelete = (ImageButton) convertView.findViewById(R.id.subkey_added_item_delete);
- convertView.setTag(holder);
-
- holder.mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- Choice newKeyAlgorithmChoice = (Choice) holder.mAlgorithmSpinner.getSelectedItem();
- // update referenced model item
- holder.mModel.mAlgorithm = newKeyAlgorithmChoice.getId();
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
+ holder.vKeyId = (TextView) convertView.findViewById(R.id.subkey_item_key_id);
+ holder.vKeyDetails = (TextView) convertView.findViewById(R.id.subkey_item_details);
+ holder.vKeyExpiry = (TextView) convertView.findViewById(R.id.subkey_item_expiry);
+ holder.vCertifyIcon = (ImageView) convertView.findViewById(R.id.subkey_item_ic_certify);
+ holder.vEncryptIcon = (ImageView) convertView.findViewById(R.id.subkey_item_ic_encrypt);
+ holder.vSignIcon = (ImageView) convertView.findViewById(R.id.subkey_item_ic_sign);
+ holder.vDelete = (ImageButton) convertView.findViewById(R.id.subkey_item_delete_button);
+ holder.vDelete.setVisibility(View.VISIBLE); // always visible
+
+ // not used:
+ ImageView editImage = (ImageView) convertView.findViewById(R.id.subkey_item_edit_image);
+ editImage.setVisibility(View.GONE);
+ ImageView revokedIcon = (ImageView) convertView.findViewById(R.id.subkey_item_ic_revoked);
+ revokedIcon.setVisibility(View.GONE);
- holder.mKeySizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- Choice newKeyAlgorithmChoice = (Choice) holder.mAlgorithmSpinner.getSelectedItem();
- int newKeySize = getProperKeyLength(newKeyAlgorithmChoice.getId(),
- getSelectedKeyLength(holder.mKeySizeSpinner, holder.mCustomKeyEditText));
- // update referenced model item
- holder.mModel.mKeysize = newKeySize;
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
+ convertView.setTag(holder);
holder.vDelete.setOnClickListener(new View.OnClickListener() {
@Override
@@ -136,226 +98,44 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
// save reference to model item
holder.mModel = getItem(position);
- // TODO
- boolean wouldBeMasterKey = false;
-// boolean wouldBeMasterKey = (childCount == 0);
-
- ArrayList<Choice> choices = new ArrayList<Choice>();
- choices.add(new Choice(Constants.choice.algorithm.dsa, mActivity.getResources().getString(
- R.string.dsa)));
- if (!wouldBeMasterKey) {
- choices.add(new Choice(Constants.choice.algorithm.elgamal, mActivity.getResources().getString(
- R.string.elgamal)));
- }
-
- choices.add(new Choice(Constants.choice.algorithm.rsa, mActivity.getResources().getString(
- R.string.rsa)));
-
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(mActivity,
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- holder.mAlgorithmSpinner.setAdapter(adapter);
- // make RSA the default
- for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == Constants.choice.algorithm.rsa) {
- holder.mAlgorithmSpinner.setSelection(i);
- break;
- }
- }
-
- // dynamic ArrayAdapter must be created (instead of ArrayAdapter.getFromResource), because it's content may change
- ArrayAdapter<CharSequence> keySizeAdapter = new ArrayAdapter<CharSequence>(mActivity, android.R.layout.simple_spinner_item,
- new ArrayList<CharSequence>(Arrays.asList(mActivity.getResources().getStringArray(R.array.rsa_key_size_spinner_values))));
- keySizeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- holder.mKeySizeSpinner.setAdapter(keySizeAdapter);
- holder.mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length
-
- holder.mCustomKeyEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
-// setOkButtonAvailability(alertDialog);
- }
- });
-
- holder.mKeySizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- setCustomKeyVisibility(holder.mKeySizeSpinner, holder.mCustomKeyEditText,
- holder.mCustomKeyTextView, holder.mCustomKeyInfoTextView);
-// setOkButtonAvailability(alertDialog);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
+ String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
+ mActivity,
+ holder.mModel.mAlgorithm,
+ holder.mModel.mKeysize
+ );
+ holder.vKeyId.setText(R.string.edit_key_new_subkey);
+ holder.vKeyDetails.setText(algorithmStr);
- holder.mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- setKeyLengthSpinnerValuesForAlgorithm(((Choice) parent.getSelectedItem()).getId(),
- holder.mKeySizeSpinner, holder.mCustomKeyInfoTextView);
+ if (holder.mModel.mExpiry != null) {
+ Date expiryDate = new Date(holder.mModel.mExpiry * 1000);
- setCustomKeyVisibility(holder.mKeySizeSpinner, holder.mCustomKeyEditText,
- holder.mCustomKeyTextView, holder.mCustomKeyInfoTextView);
-// setOkButtonAvailability(alertDialog);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-//
-// holder.vAddress.setText(holder.mModel.address);
-// holder.vAddress.setThreshold(1); // Start working from first character
-// holder.vAddress.setAdapter(mAutoCompleteEmailAdapter);
-//
-// holder.vName.setText(holder.mModel.name);
-// holder.vName.setThreshold(1); // Start working from first character
-// holder.vName.setAdapter(mAutoCompleteNameAdapter);
-//
-// holder.vComment.setText(holder.mModel.comment);
-
- return convertView;
- }
-
-
- private int getSelectedKeyLength(Spinner keySizeSpinner, EditText customKeyEditText) {
- final String selectedItemString = (String) keySizeSpinner.getSelectedItem();
- final String customLengthString = mActivity.getResources().getString(R.string.key_size_custom);
- final boolean customSelected = customLengthString.equals(selectedItemString);
- String keyLengthString = customSelected ? customKeyEditText.getText().toString() : selectedItemString;
- int keySize;
- try {
- keySize = Integer.parseInt(keyLengthString);
- } catch (NumberFormatException e) {
- keySize = 0;
- }
- return keySize;
- }
-
- /**
- * <h3>RSA</h3>
- * <p>for RSA algorithm, key length must be greater than 1024 (according to
- * <a href="https://github.com/open-keychain/open-keychain/issues/102">#102</a>). Possibility to generate keys bigger
- * than 8192 bits is currently disabled, because it's almost impossible to generate them on a mobile device (check
- * <a href="http://www.javamex.com/tutorials/cryptography/rsa_key_length.shtml">RSA key length plot</a> and
- * <a href="http://www.keylength.com/">Cryptographic Key Length Recommendation</a>). Also, key length must be a
- * multiplicity of 8.</p>
- * <h3>ElGamal</h3>
- * <p>For ElGamal algorithm, supported key lengths are 1536, 2048, 3072, 4096 or 8192 bits.</p>
- * <h3>DSA</h3>
- * <p>For DSA algorithm key length must be between 512 and 1024. Also, it must me dividable by 64.</p>
- *
- * @return correct key length, according to SpongyCastle specification. Returns <code>-1</code>, if key length is
- * inappropriate.
- */
- private int getProperKeyLength(int algorithmId, int currentKeyLength) {
- final int[] elGamalSupportedLengths = {1536, 2048, 3072, 4096, 8192};
- int properKeyLength = -1;
- switch (algorithmId) {
- case Constants.choice.algorithm.rsa:
- if (currentKeyLength > 1024 && currentKeyLength <= 8192) {
- properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8);
- }
- break;
- case Constants.choice.algorithm.elgamal:
- int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length];
- for (int i = 0; i < elGamalSupportedLengths.length; i++) {
- elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength);
- }
- int minimalValue = Integer.MAX_VALUE;
- int minimalIndex = -1;
- for (int i = 0; i < elGammalKeyDiff.length; i++) {
- if (elGammalKeyDiff[i] <= minimalValue) {
- minimalValue = elGammalKeyDiff[i];
- minimalIndex = i;
- }
- }
- properKeyLength = elGamalSupportedLengths[minimalIndex];
- break;
- case Constants.choice.algorithm.dsa:
- if (currentKeyLength >= 512 && currentKeyLength <= 1024) {
- properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64);
- }
- break;
- }
- return properKeyLength;
- }
-
- // TODO: make this an error message on the field
-// private boolean setOkButtonAvailability(AlertDialog alertDialog) {
-// final Choice selectedAlgorithm = (Choice) mAlgorithmSpinner.getSelectedItem();
-// final int selectedKeySize = getSelectedKeyLength(); //Integer.parseInt((String) mKeySizeSpinner.getSelectedItem());
-// final int properKeyLength = getProperKeyLength(selectedAlgorithm.getId(), selectedKeySize);
-// alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(properKeyLength > 0);
-// }
-
- private void setCustomKeyVisibility(Spinner keySizeSpinner, EditText customkeyedittext, TextView customKeyTextView, TextView customKeyInfoTextView) {
- final String selectedItemString = (String) keySizeSpinner.getSelectedItem();
- final String customLengthString = mActivity.getResources().getString(R.string.key_size_custom);
- final boolean customSelected = customLengthString.equals(selectedItemString);
- final int visibility = customSelected ? View.VISIBLE : View.GONE;
-
- customkeyedittext.setVisibility(visibility);
- customKeyTextView.setVisibility(visibility);
- customKeyInfoTextView.setVisibility(visibility);
-
- // hide keyboard after setting visibility to gone
- if (visibility == View.GONE) {
- InputMethodManager imm = (InputMethodManager)
- mActivity.getSystemService(mActivity.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(customkeyedittext.getWindowToken(), 0);
+ holder.vKeyExpiry.setText(getContext().getString(R.string.label_expiry) + ": "
+ + DateFormat.getDateFormat(getContext()).format(expiryDate));
+ } else {
+ holder.vKeyExpiry.setText(getContext().getString(R.string.label_expiry) + ": "
+ + getContext().getString(R.string.none));
}
- }
- private void setKeyLengthSpinnerValuesForAlgorithm(int algorithmId, Spinner keySizeSpinner, TextView customKeyInfoTextView) {
- final ArrayAdapter<CharSequence> keySizeAdapter = (ArrayAdapter<CharSequence>) keySizeSpinner.getAdapter();
- final Object selectedItem = keySizeSpinner.getSelectedItem();
- keySizeAdapter.clear();
- switch (algorithmId) {
- case Constants.choice.algorithm.rsa:
- replaceArrayAdapterContent(keySizeAdapter, R.array.rsa_key_size_spinner_values);
- customKeyInfoTextView.setText(mActivity.getResources().getString(R.string.key_size_custom_info_rsa));
- break;
- case Constants.choice.algorithm.elgamal:
- replaceArrayAdapterContent(keySizeAdapter, R.array.elgamal_key_size_spinner_values);
- customKeyInfoTextView.setText(""); // ElGamal does not support custom key length
- break;
- case Constants.choice.algorithm.dsa:
- replaceArrayAdapterContent(keySizeAdapter, R.array.dsa_key_size_spinner_values);
- customKeyInfoTextView.setText(mActivity.getResources().getString(R.string.key_size_custom_info_dsa));
- break;
+ int flags = holder.mModel.mFlags;
+ if ((flags & KeyFlags.CERTIFY_OTHER) > 0) {
+ holder.vCertifyIcon.setVisibility(View.VISIBLE);
+ } else {
+ holder.vCertifyIcon.setVisibility(View.GONE);
}
- keySizeAdapter.notifyDataSetChanged();
-
- // when switching algorithm, try to select same key length as before
- for (int i = 0; i < keySizeAdapter.getCount(); i++) {
- if (selectedItem.equals(keySizeAdapter.getItem(i))) {
- keySizeSpinner.setSelection(i);
- break;
- }
+ if ((flags & KeyFlags.SIGN_DATA) > 0) {
+ holder.vSignIcon.setVisibility(View.VISIBLE);
+ } else {
+ holder.vSignIcon.setVisibility(View.GONE);
}
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- private void replaceArrayAdapterContent(ArrayAdapter<CharSequence> arrayAdapter, int stringArrayResourceId) {
- final String[] spinnerValuesStringArray = mActivity.getResources().getStringArray(stringArrayResourceId);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- arrayAdapter.addAll(spinnerValuesStringArray);
+ if (((flags & KeyFlags.ENCRYPT_COMMS) > 0)
+ || ((flags & KeyFlags.ENCRYPT_STORAGE) > 0)) {
+ holder.vEncryptIcon.setVisibility(View.VISIBLE);
} else {
- for (final String value : spinnerValuesStringArray) {
- arrayAdapter.add(value);
- }
+ holder.vEncryptIcon.setVisibility(View.GONE);
}
+ // TODO: missing icon for authenticate
+
+ return convertView;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
index ee3341c08..9bf47a387 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
@@ -252,6 +252,26 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
return mCursor.getString(INDEX_USER_ID);
}
+ public boolean getIsRevoked(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(INDEX_IS_REVOKED) > 0;
+ }
+
+ public boolean getIsRevokedPending(int position) {
+ mCursor.moveToPosition(position);
+ String userId = mCursor.getString(INDEX_USER_ID);
+
+ boolean isRevokedPending = false;
+ if (mSaveKeyringParcel != null) {
+ if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) {
+ isRevokedPending = true;
+ }
+
+ }
+
+ return isRevokedPending;
+ }
+
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.view_key_user_id_item, null);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
index 920743a9b..cb31978e9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
@@ -27,46 +27,64 @@ import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.text.Editable;
import android.text.TextWatcher;
+import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.Spinner;
+import android.widget.TableRow;
import android.widget.TextView;
-import org.sufficientlysecure.keychain.Constants;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
+import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.Choice;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
-public class CreateKeyDialogFragment extends DialogFragment {
+public class AddSubkeyDialogFragment extends DialogFragment {
public interface OnAlgorithmSelectedListener {
- public void onAlgorithmSelected(Choice algorithmChoice, int keySize);
+ public void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey);
}
- private static final String ARG_EDITOR_CHILD_COUNT = "child_count";
+ private static final String ARG_WILL_BE_MASTER_KEY = "will_be_master_key";
private OnAlgorithmSelectedListener mAlgorithmSelectedListener;
+
+ private CheckBox mNoExpiryCheckBox;
+ private TableRow mExpiryRow;
+ private DatePicker mExpiryDatePicker;
private Spinner mAlgorithmSpinner;
private Spinner mKeySizeSpinner;
private TextView mCustomKeyTextView;
private EditText mCustomKeyEditText;
private TextView mCustomKeyInfoTextView;
+ private CheckBox mFlagCertify;
+ private CheckBox mFlagSign;
+ private CheckBox mFlagEncrypt;
+ private CheckBox mFlagAuthenticate;
public void setOnAlgorithmSelectedListener(OnAlgorithmSelectedListener listener) {
mAlgorithmSelectedListener = listener;
}
- public static CreateKeyDialogFragment newInstance(int mEditorChildCount) {
- CreateKeyDialogFragment frag = new CreateKeyDialogFragment();
+ public static AddSubkeyDialogFragment newInstance(boolean willBeMasterKey) {
+ AddSubkeyDialogFragment frag = new AddSubkeyDialogFragment();
Bundle args = new Bundle();
- args.putInt(ARG_EDITOR_CHILD_COUNT, mEditorChildCount);
+ args.putBoolean(ARG_WILL_BE_MASTER_KEY, willBeMasterKey);
frag.setArguments(args);
@@ -78,42 +96,64 @@ public class CreateKeyDialogFragment extends DialogFragment {
final FragmentActivity context = getActivity();
final LayoutInflater mInflater;
- final int childCount = getArguments().getInt(ARG_EDITOR_CHILD_COUNT);
+ final boolean willBeMasterKey = getArguments().getBoolean(ARG_WILL_BE_MASTER_KEY);
mInflater = context.getLayoutInflater();
CustomAlertDialogBuilder dialog = new CustomAlertDialogBuilder(context);
- View view = mInflater.inflate(R.layout.create_key_dialog, null);
+ View view = mInflater.inflate(R.layout.add_subkey_dialog, null);
dialog.setView(view);
- dialog.setTitle(R.string.title_create_key);
+ dialog.setTitle(R.string.title_add_subkey);
+
+ mNoExpiryCheckBox = (CheckBox) view.findViewById(R.id.add_subkey_no_expiry);
+ mExpiryRow = (TableRow) view.findViewById(R.id.add_subkey_expiry_row);
+ mExpiryDatePicker = (DatePicker) view.findViewById(R.id.add_subkey_expiry_date_picker);
+ mAlgorithmSpinner = (Spinner) view.findViewById(R.id.add_subkey_algorithm);
+ mKeySizeSpinner = (Spinner) view.findViewById(R.id.add_subkey_size);
+ mCustomKeyTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_label);
+ mCustomKeyEditText = (EditText) view.findViewById(R.id.add_subkey_custom_key_size_input);
+ mCustomKeyInfoTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_info);
+ mFlagCertify = (CheckBox) view.findViewById(R.id.add_subkey_flag_certify);
+ mFlagSign = (CheckBox) view.findViewById(R.id.add_subkey_flag_sign);
+ mFlagEncrypt = (CheckBox) view.findViewById(R.id.add_subkey_flag_encrypt);
+ mFlagAuthenticate = (CheckBox) view.findViewById(R.id.add_subkey_flag_authenticate);
+
+ mNoExpiryCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ mExpiryRow.setVisibility(View.GONE);
+ } else {
+ mExpiryRow.setVisibility(View.VISIBLE);
+ }
+ }
+ });
- boolean wouldBeMasterKey = (childCount == 0);
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+ mExpiryDatePicker.setMinDate(new Date().getTime() + DateUtils.DAY_IN_MILLIS);
+ }
- mAlgorithmSpinner = (Spinner) view.findViewById(R.id.create_key_algorithm);
ArrayList<Choice> choices = new ArrayList<Choice>();
- choices.add(new Choice(Constants.choice.algorithm.dsa, getResources().getString(
+ choices.add(new Choice(PublicKeyAlgorithmTags.DSA, getResources().getString(
R.string.dsa)));
- if (!wouldBeMasterKey) {
- choices.add(new Choice(Constants.choice.algorithm.elgamal, getResources().getString(
+ if (!willBeMasterKey) {
+ choices.add(new Choice(PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, getResources().getString(
R.string.elgamal)));
}
-
- choices.add(new Choice(Constants.choice.algorithm.rsa, getResources().getString(
+ choices.add(new Choice(PublicKeyAlgorithmTags.RSA_GENERAL, 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);
mAlgorithmSpinner.setAdapter(adapter);
// make RSA the default
for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == Constants.choice.algorithm.rsa) {
+ if (choices.get(i).getId() == PublicKeyAlgorithmTags.RSA_GENERAL) {
mAlgorithmSpinner.setSelection(i);
break;
}
}
- mKeySizeSpinner = (Spinner) view.findViewById(R.id.create_key_size);
// dynamic ArrayAdapter must be created (instead of ArrayAdapter.getFromResource), because it's content may change
ArrayAdapter<CharSequence> keySizeAdapter = new ArrayAdapter<CharSequence>(context, android.R.layout.simple_spinner_item,
new ArrayList<CharSequence>(Arrays.asList(getResources().getStringArray(R.array.rsa_key_size_spinner_values))));
@@ -121,9 +161,6 @@ public class CreateKeyDialogFragment extends DialogFragment {
mKeySizeSpinner.setAdapter(keySizeAdapter);
mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length
- mCustomKeyTextView = (TextView) view.findViewById(R.id.custom_key_size_label);
- mCustomKeyEditText = (EditText) view.findViewById(R.id.custom_key_size_input);
- mCustomKeyInfoTextView = (TextView) view.findViewById(R.id.custom_key_size_info);
dialog.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@@ -131,7 +168,39 @@ public class CreateKeyDialogFragment extends DialogFragment {
di.dismiss();
Choice newKeyAlgorithmChoice = (Choice) mAlgorithmSpinner.getSelectedItem();
int newKeySize = getProperKeyLength(newKeyAlgorithmChoice.getId(), getSelectedKeyLength());
- mAlgorithmSelectedListener.onAlgorithmSelected(newKeyAlgorithmChoice, newKeySize);
+
+ int flags = 0;
+ if (mFlagCertify.isChecked()) {
+ flags |= KeyFlags.CERTIFY_OTHER;
+ }
+ if (mFlagSign.isChecked()) {
+ flags |= KeyFlags.SIGN_DATA;
+ }
+ if (mFlagEncrypt.isChecked()) {
+ flags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
+ }
+ if (mFlagAuthenticate.isChecked()) {
+ flags |= KeyFlags.AUTHENTICATION;
+ }
+
+ Long expiry;
+ if (mNoExpiryCheckBox.isChecked()) {
+ expiry = null;
+ } else {
+ Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ //noinspection ResourceType
+ selectedCal.set(mExpiryDatePicker.getYear(),
+ mExpiryDatePicker.getMonth(), mExpiryDatePicker.getDayOfMonth());
+ expiry = selectedCal.getTime().getTime() / 1000;
+ }
+
+ SaveKeyringParcel.SubkeyAdd newSubkey = new SaveKeyringParcel.SubkeyAdd(
+ newKeyAlgorithmChoice.getId(),
+ newKeySize,
+ flags,
+ expiry
+ );
+ mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey);
}
}
);
@@ -142,7 +211,8 @@ public class CreateKeyDialogFragment extends DialogFragment {
public void onClick(DialogInterface di, int id) {
di.dismiss();
}
- });
+ }
+ );
final AlertDialog alertDialog = dialog.show();
@@ -224,12 +294,12 @@ public class CreateKeyDialogFragment extends DialogFragment {
final int[] elGamalSupportedLengths = {1536, 2048, 3072, 4096, 8192};
int properKeyLength = -1;
switch (algorithmId) {
- case Constants.choice.algorithm.rsa:
- if (currentKeyLength > 1024 && currentKeyLength <= 8192) {
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ if (currentKeyLength > 1024 && currentKeyLength <= 16384) {
properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8);
}
break;
- case Constants.choice.algorithm.elgamal:
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length];
for (int i = 0; i < elGamalSupportedLengths.length; i++) {
elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength);
@@ -244,7 +314,7 @@ public class CreateKeyDialogFragment extends DialogFragment {
}
properKeyLength = elGamalSupportedLengths[minimalIndex];
break;
- case Constants.choice.algorithm.dsa:
+ case PublicKeyAlgorithmTags.DSA:
if (currentKeyLength >= 512 && currentKeyLength <= 1024) {
properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64);
}
@@ -283,15 +353,15 @@ public class CreateKeyDialogFragment extends DialogFragment {
final Object selectedItem = mKeySizeSpinner.getSelectedItem();
keySizeAdapter.clear();
switch (algorithmId) {
- case Constants.choice.algorithm.rsa:
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
replaceArrayAdapterContent(keySizeAdapter, R.array.rsa_key_size_spinner_values);
mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_rsa));
break;
- case Constants.choice.algorithm.elgamal:
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
replaceArrayAdapterContent(keySizeAdapter, R.array.elgamal_key_size_spinner_values);
mCustomKeyInfoTextView.setText(""); // ElGamal does not support custom key length
break;
- case Constants.choice.algorithm.dsa:
+ case PublicKeyAlgorithmTags.DSA:
replaceArrayAdapterContent(keySizeAdapter, R.array.dsa_key_size_spinner_values);
mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_dsa));
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java
index d5264ae10..4d6b13476 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java
@@ -29,7 +29,6 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.text.Editable;
-import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.KeyEvent;
@@ -149,6 +148,14 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA
}
});
+ mName.setThreshold(1); // Start working from first character
+ mName.setAdapter(
+ new ArrayAdapter<String>
+ (getActivity(), android.R.layout.simple_spinner_dropdown_item,
+ ContactHelper.getPossibleUserNames(getActivity())
+ )
+ );
+
alert.setNegativeButton(android.R.string.cancel, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java
deleted file mode 100644
index d5354a9f6..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * 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.dialog;
-
-import android.app.DatePickerDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.support.v4.app.DialogFragment;
-import android.text.format.DateUtils;
-import android.widget.DatePicker;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class ChangeExpiryDialogFragment extends DialogFragment {
- private static final String ARG_MESSENGER = "messenger";
- private static final String ARG_CREATION_DATE = "creation_date";
- private static final String ARG_EXPIRY_DATE = "expiry_date";
-
- public static final int MESSAGE_NEW_EXPIRY_DATE = 1;
- public static final String MESSAGE_DATA_EXPIRY_DATE = "expiry_date";
-
- private Messenger mMessenger;
- private Calendar mCreationCal;
- private Calendar mExpiryCal;
-
- private int mDatePickerResultCount = 0;
- private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
- new DatePickerDialog.OnDateSetListener() {
- public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
- // Note: Ignore results after the first one - android sends multiples.
- if (mDatePickerResultCount++ == 0) {
- Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- selectedCal.set(year, monthOfYear, dayOfMonth);
- if (mExpiryCal != null) {
- long numDays = (selectedCal.getTimeInMillis() / 86400000)
- - (mExpiryCal.getTimeInMillis() / 86400000);
- if (numDays > 0) {
- Bundle data = new Bundle();
- data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime());
- sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
- }
- } else {
- Bundle data = new Bundle();
- data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime());
- sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
- }
- }
- }
- };
-
- public 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));
- }
- }
-
- /**
- * Creates new instance of this dialog fragment
- */
- public static ChangeExpiryDialogFragment newInstance(Messenger messenger,
- Date creationDate, Date expiryDate) {
- ChangeExpiryDialogFragment frag = new ChangeExpiryDialogFragment();
- Bundle args = new Bundle();
- args.putParcelable(ARG_MESSENGER, messenger);
- args.putSerializable(ARG_CREATION_DATE, creationDate);
- args.putSerializable(ARG_EXPIRY_DATE, expiryDate);
-
- frag.setArguments(args);
-
- return frag;
- }
-
- /**
- * Creates dialog
- */
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- mMessenger = getArguments().getParcelable(ARG_MESSENGER);
- Date creationDate = (Date) getArguments().getSerializable(ARG_CREATION_DATE);
- Date expiryDate = (Date) getArguments().getSerializable(ARG_EXPIRY_DATE);
-
- mCreationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- mCreationCal.setTime(creationDate);
- mExpiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- mExpiryCal.setTime(expiryDate);
-
- /*
- * 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(getActivity(),
- mExpiryDateSetListener, mExpiryCal.get(Calendar.YEAR), mExpiryCal.get(Calendar.MONTH),
- mExpiryCal.get(Calendar.DAY_OF_MONTH));
- mDatePickerResultCount = 0;
- dialog.setCancelable(true);
- dialog.setButton(Dialog.BUTTON_NEGATIVE,
- getActivity().getString(R.string.btn_no_date),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // Note: Ignore results after the first one - android sends multiples.
- if (mDatePickerResultCount++ == 0) {
- // none expiry dates corresponds to a null message
- Bundle data = new Bundle();
- data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, null);
- sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
- }
- }
- }
- );
-
- // 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) {
- // will crash with IllegalArgumentException if we set a min date
- // that is not before expiry
- if (mCreationCal != null && mCreationCal.before(mExpiryCal)) {
- dialog.getDatePicker().setMinDate(mCreationCal.getTime().getTime()
- + DateUtils.DAY_IN_MILLIS);
- } else {
- // When created date isn't available
- dialog.getDatePicker().setMinDate(mExpiryCal.getTime().getTime()
- + DateUtils.DAY_IN_MILLIS);
- }
- }
-
- return dialog;
- }
-
- /**
- * Send message back to handler which is initialized in a activity
- *
- * @param what Message integer you want to send
- */
- private void sendMessageToHandler(Integer what, Bundle data) {
- Message msg = Message.obtain();
- msg.what = what;
- if (data != null) {
- msg.setData(data);
- }
-
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
- } catch (NullPointerException e) {
- Log.w(Constants.TAG, "Messenger is null!", e);
- }
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
index cae6cf043..5f29f1d18 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
@@ -18,43 +18,24 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Dialog;
-import android.app.ProgressDialog;
import android.content.DialogInterface;
-import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.provider.DocumentsContract;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.widget.Toast;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.helper.FileHelper;
public class DeleteFileDialogFragment extends DialogFragment {
- private static final String ARG_DELETE_FILE = "delete_file";
private static final String ARG_DELETE_URI = "delete_uri";
/**
* Creates new instance of this delete file dialog fragment
*/
- public static DeleteFileDialogFragment newInstance(String deleteFile) {
- DeleteFileDialogFragment frag = new DeleteFileDialogFragment();
- Bundle args = new Bundle();
-
- args.putString(ARG_DELETE_FILE, deleteFile);
-
- frag.setArguments(args);
-
- return frag;
- }
-
- /**
- * Creates new instance of this delete file dialog fragment
- */
public static DeleteFileDialogFragment newInstance(Uri deleteUri) {
DeleteFileDialogFragment frag = new DeleteFileDialogFragment();
Bundle args = new Bundle();
@@ -73,15 +54,15 @@ public class DeleteFileDialogFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
- final Uri deleteUri = getArguments().containsKey(ARG_DELETE_URI) ? getArguments().<Uri>getParcelable(ARG_DELETE_URI) : null;
- final String deleteFile = getArguments().getString(ARG_DELETE_FILE);
+ final Uri deleteUri = getArguments().getParcelable(ARG_DELETE_URI);
+ final String deleteFilename = FileHelper.getFilename(getActivity(), deleteUri);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
alert.setTitle(R.string.warning);
- alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFile));
+ alert.setMessage(this.getString(R.string.file_delete_confirmation, deleteFilename));
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@@ -89,51 +70,23 @@ public class DeleteFileDialogFragment extends DialogFragment {
public void onClick(DialogInterface dialog, int id) {
dismiss();
- if (deleteUri != null) {
- // We can not securely delete Documents, so just use usual delete on them
- DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri);
- return;
- }
-
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(activity, KeychainIntentService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- intent.setAction(KeychainIntentService.ACTION_DELETE_FILE_SECURELY);
- data.putString(KeychainIntentService.DELETE_FILE, deleteFile);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance(
- getString(R.string.progress_deleting_securely),
- ProgressDialog.STYLE_HORIZONTAL,
- false,
- null);
-
- // Message is received after deleting is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler =
- new KeychainIntentServiceHandler(activity, deletingDialog) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- Toast.makeText(activity, R.string.file_delete_successful,
- Toast.LENGTH_SHORT).show();
- }
+ // We can not securely delete Uris, so just use usual delete on them
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ if (DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri)) {
+ Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show();
+ return;
}
- };
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ if (getActivity().getContentResolver().delete(deleteUri, null, null) > 0) {
+ Toast.makeText(getActivity(), R.string.file_delete_successful, Toast.LENGTH_SHORT).show();
+ return;
+ }
- // show progress dialog
- deletingDialog.show(activity.getSupportFragmentManager(), "deletingDialog");
+ Toast.makeText(getActivity(), getActivity().getString(R.string.error_file_delete_failed, deleteFilename), Toast.LENGTH_SHORT).show();
- // start service with intent
- activity.startService(intent);
+ // Note: We can't delete every file...
+ // If possible we should find out if deletion is possible before even showing the option to do so.
}
});
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 01d2fae6a..4927a4d24 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -123,7 +123,7 @@ public class DeleteKeyDialogFragment extends DialogFragment {
boolean success = false;
for (long masterKeyId : masterKeyIds) {
int count = activity.getContentResolver().delete(
- KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null
+ KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null
);
success = count > 0;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java
new file mode 100644
index 000000000..aa63f9944
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java
@@ -0,0 +1,187 @@
+/*
+ * 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.dialog;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.v4.app.DialogFragment;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.DatePicker;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class EditSubkeyExpiryDialogFragment extends DialogFragment {
+ private static final String ARG_MESSENGER = "messenger";
+ private static final String ARG_CREATION_DATE = "creation_date";
+ private static final String ARG_EXPIRY_DATE = "expiry_date";
+
+ public static final int MESSAGE_NEW_EXPIRY_DATE = 1;
+ public static final int MESSAGE_CANCEL = 2;
+ public static final String MESSAGE_DATA_EXPIRY_DATE = "expiry_date";
+
+ private Messenger mMessenger;
+ private Calendar mExpiryCal;
+
+ private DatePicker mDatePicker;
+
+ /**
+ * Creates new instance of this dialog fragment
+ */
+ public static EditSubkeyExpiryDialogFragment newInstance(Messenger messenger,
+ Long creationDate, Long expiryDate) {
+ EditSubkeyExpiryDialogFragment frag = new EditSubkeyExpiryDialogFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_MESSENGER, messenger);
+ args.putSerializable(ARG_CREATION_DATE, creationDate);
+ args.putSerializable(ARG_EXPIRY_DATE, expiryDate);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * Creates dialog
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Activity activity = getActivity();
+ mMessenger = getArguments().getParcelable(ARG_MESSENGER);
+ Date creationDate = new Date(getArguments().getLong(ARG_CREATION_DATE) * 1000);
+ Date expiryDate = new Date(getArguments().getLong(ARG_EXPIRY_DATE) * 1000);
+
+ Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ creationCal.setTime(creationDate);
+ mExpiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ mExpiryCal.setTime(expiryDate);
+
+ Log.d(Constants.TAG, "onCreateDialog");
+
+ // Explicitly not using DatePickerDialog here!
+ // DatePickerDialog is difficult to customize and has many problems (see old git versions)
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
+
+ alert.setTitle(R.string.expiry_date_dialog_title);
+
+ LayoutInflater inflater = activity.getLayoutInflater();
+ View view = inflater.inflate(R.layout.edit_subkey_expiry_dialog, null);
+ alert.setView(view);
+
+ mDatePicker = (DatePicker) view.findViewById(R.id.edit_subkey_expiry_date_picker);
+
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+ // will crash with IllegalArgumentException if we set a min date
+ // that is not before expiry
+ if (creationCal.before(mExpiryCal)) {
+ mDatePicker.setMinDate(creationCal.getTime().getTime()
+ + DateUtils.DAY_IN_MILLIS);
+ } else {
+ // when creation date isn't available
+ mDatePicker.setMinDate(mExpiryCal.getTime().getTime()
+ + DateUtils.DAY_IN_MILLIS);
+ }
+ }
+
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dismiss();
+
+ Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ //noinspection ResourceType
+ selectedCal.set(mDatePicker.getYear(), mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
+
+ if (mExpiryCal != null) {
+ long numDays = (selectedCal.getTimeInMillis() / 86400000)
+ - (mExpiryCal.getTimeInMillis() / 86400000);
+ if (numDays > 0) {
+ Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime().getTime() / 1000);
+ sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
+ }
+ } else {
+ Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime().getTime() / 1000);
+ sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
+ }
+ }
+ });
+
+ alert.setNeutralButton(R.string.btn_no_date, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dismiss();
+
+ Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, null);
+ sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
+ }
+ });
+
+ alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dismiss();
+ }
+ });
+
+ return alert.show();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+
+ dismiss();
+ sendMessageToHandler(MESSAGE_CANCEL, null);
+ }
+
+ /**
+ * Send message back to handler which is initialized in a activity
+ *
+ * @param what Message integer you want to send
+ */
+ private void sendMessageToHandler(Integer what, Bundle data) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ if (data != null) {
+ msg.setData(data);
+ }
+
+ try {
+ mMessenger.send(msg);
+ } catch (RemoteException e) {
+ Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
+ } catch (NullPointerException e) {
+ Log.w(Constants.TAG, "Messenger is null!", e);
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditUserIdDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditUserIdDialogFragment.java
index 5eba3a463..70a3b8fd0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditUserIdDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditUserIdDialogFragment.java
@@ -32,6 +32,9 @@ import org.sufficientlysecure.keychain.util.Log;
public class EditUserIdDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
+ private static final String ARG_IS_REVOKED = "is_revoked";
+ private static final String ARG_IS_REVOKED_PENDING = "is_revoked_pending";
+
public static final int MESSAGE_CHANGE_PRIMARY_USER_ID = 1;
public static final int MESSAGE_REVOKE = 2;
@@ -40,10 +43,13 @@ public class EditUserIdDialogFragment extends DialogFragment {
/**
* Creates new instance of this dialog fragment
*/
- public static EditUserIdDialogFragment newInstance(Messenger messenger) {
+ public static EditUserIdDialogFragment newInstance(Messenger messenger, boolean isRevoked,
+ boolean isRevokedPending) {
EditUserIdDialogFragment frag = new EditUserIdDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
+ args.putBoolean(ARG_IS_REVOKED, isRevoked);
+ args.putBoolean(ARG_IS_REVOKED_PENDING, isRevokedPending);
frag.setArguments(args);
@@ -56,27 +62,49 @@ public class EditUserIdDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
+ boolean isRevoked = getArguments().getBoolean(ARG_IS_REVOKED);
+ boolean isRevokedPending = getArguments().getBoolean(ARG_IS_REVOKED_PENDING);
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(getActivity());
- CharSequence[] array = getResources().getStringArray(R.array.edit_key_edit_user_id);
-
builder.setTitle(R.string.edit_key_edit_user_id_title);
- builder.setItems(array, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- switch (which) {
- case 0:
- sendMessageToHandler(MESSAGE_CHANGE_PRIMARY_USER_ID, null);
- break;
- case 1:
- sendMessageToHandler(MESSAGE_REVOKE, null);
- break;
- default:
- break;
+ if (isRevokedPending) {
+ CharSequence[] array = getResources().getStringArray(R.array.edit_key_edit_user_id_revert_revocation);
+
+ builder.setItems(array, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case 0:
+ sendMessageToHandler(MESSAGE_REVOKE, null);
+ break;
+ default:
+ break;
+ }
}
- }
- });
+ });
+ } else if (isRevoked) {
+ builder.setMessage(R.string.edit_key_edit_user_id_revoked);
+ } else {
+ CharSequence[] array = getResources().getStringArray(R.array.edit_key_edit_user_id);
+
+ builder.setItems(array, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case 0:
+ sendMessageToHandler(MESSAGE_CHANGE_PRIMARY_USER_ID, null);
+ break;
+ case 1:
+ sendMessageToHandler(MESSAGE_REVOKE, null);
+ break;
+ default:
+ break;
+ }
+ }
+ });
+ }
+
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
index 448787ee2..18f134594 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
@@ -18,18 +18,15 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.provider.OpenableColumns;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -42,7 +39,13 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
+import java.io.File;
+
+/**
+ * This is a file chooser dialog no longer used with KitKat
+ */
public class FileDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_TITLE = "title";
@@ -52,8 +55,7 @@ public class FileDialogFragment extends DialogFragment {
public static final int MESSAGE_OKAY = 1;
- public static final String MESSAGE_DATA_URI = "uri";
- public static final String MESSAGE_DATA_FILENAME = "filename";
+ public static final String MESSAGE_DATA_FILE = "file";
public static final String MESSAGE_DATA_CHECKED = "checked";
private Messenger mMessenger;
@@ -63,8 +65,7 @@ public class FileDialogFragment extends DialogFragment {
private CheckBox mCheckBox;
private TextView mMessageTextView;
- private String mOutputFilename;
- private Uri mOutputUri;
+ private File mFile;
private static final int REQUEST_CODE = 0x00007004;
@@ -72,14 +73,14 @@ public class FileDialogFragment extends DialogFragment {
* Creates new instance of this file dialog fragment
*/
public static FileDialogFragment newInstance(Messenger messenger, String title, String message,
- String defaultFile, String checkboxText) {
+ File defaultFile, String checkboxText) {
FileDialogFragment frag = new FileDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
args.putString(ARG_TITLE, title);
args.putString(ARG_MESSAGE, message);
- args.putString(ARG_DEFAULT_FILE, defaultFile);
+ args.putString(ARG_DEFAULT_FILE, defaultFile.getAbsolutePath());
args.putString(ARG_CHECKBOX_TEXT, checkboxText);
frag.setArguments(args);
@@ -98,7 +99,11 @@ public class FileDialogFragment extends DialogFragment {
String title = getArguments().getString(ARG_TITLE);
String message = getArguments().getString(ARG_MESSAGE);
- mOutputFilename = getArguments().getString(ARG_DEFAULT_FILE);
+ mFile = new File(getArguments().getString(ARG_DEFAULT_FILE));
+ if (!mFile.isAbsolute()) {
+ // We use OK dir by default
+ mFile = new File(Constants.Path.APP_DIR.getAbsolutePath(), mFile.getName());
+ }
String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT);
LayoutInflater inflater = (LayoutInflater) activity
@@ -112,18 +117,14 @@ public class FileDialogFragment extends DialogFragment {
mMessageTextView.setText(message);
mFilename = (EditText) view.findViewById(R.id.input);
- mFilename.setText(mOutputFilename);
+ mFilename.setText(mFile.getName());
mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// only .asc or .gpg files
// setting it to text/plain prevents Cynaogenmod's file manager from selecting asc
// or gpg types!
- if (Constants.KITKAT) {
- FileHelper.saveDocument(FileDialogFragment.this, mOutputUri, "*/*", REQUEST_CODE);
- } else {
- FileHelper.openFile(FileDialogFragment.this, mOutputFilename, "*/*", REQUEST_CODE);
- }
+ FileHelper.openFile(FileDialogFragment.this, Uri.fromFile(mFile), "*/*", REQUEST_CODE);
}
});
@@ -146,19 +147,23 @@ public class FileDialogFragment extends DialogFragment {
dismiss();
String currentFilename = mFilename.getText().toString();
- if (mOutputFilename == null || !mOutputFilename.equals(currentFilename)) {
- mOutputUri = null;
- mOutputFilename = mFilename.getText().toString();
+ if (currentFilename == null || currentFilename.isEmpty()) {
+ // No file is like pressing cancel, UI: maybe disable positive button in this case?
+ return;
+ }
+
+ if (mFile == null || currentFilename.startsWith("/")) {
+ mFile = new File(currentFilename);
+ } else if (!mFile.getName().equals(currentFilename)) {
+ // We update our File object if user changed name!
+ mFile = new File(mFile.getParentFile(), currentFilename);
}
boolean checked = mCheckBox.isEnabled() && mCheckBox.isChecked();
// return resulting data back to activity
Bundle data = new Bundle();
- if (mOutputUri != null) {
- data.putParcelable(MESSAGE_DATA_URI, mOutputUri);
- }
- data.putString(MESSAGE_DATA_FILENAME, mFilename.getText().toString());
+ data.putString(MESSAGE_DATA_FILE, mFile.getAbsolutePath());
data.putBoolean(MESSAGE_DATA_CHECKED, checked);
sendMessageToHandler(MESSAGE_OKAY, data);
@@ -175,44 +180,17 @@ public class FileDialogFragment extends DialogFragment {
return alert.show();
}
- /**
- * Updates filename in dialog, normally called in onActivityResult in activity using the
- * FileDialog
- */
- private void setFilename(String filename) {
- AlertDialog dialog = (AlertDialog) getDialog();
- EditText filenameEditText = (EditText) dialog.findViewById(R.id.input);
-
- if (filenameEditText != null) {
- filenameEditText.setText(filename);
- }
- }
-
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode & 0xFFFF) {
case REQUEST_CODE: {
if (resultCode == Activity.RESULT_OK && data != null) {
- if (Constants.KITKAT) {
- mOutputUri = data.getData();
- Cursor cursor = getActivity().getContentResolver().query(mOutputUri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
- if (cursor != null) {
- if (cursor.moveToNext()) {
- mOutputFilename = cursor.getString(0);
- mFilename.setText(mOutputFilename);
- }
- cursor.close();
- }
+ File file = new File(data.getData().getPath());
+ if (file.getParentFile().exists()) {
+ mFile = file;
+ mFilename.setText(mFile.getName());
} else {
- try {
- String path = data.getData().getPath();
- Log.d(Constants.TAG, "path=" + path);
-
- // set filename used in export/import dialogs
- setFilename(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!", e);
- }
+ Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
index 1386ed098..5e2bec0e9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
@@ -31,7 +31,6 @@ import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
new file mode 100644
index 000000000..7e762fe77
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
@@ -0,0 +1,271 @@
+/*
+ * 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.widget;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.tokenautocomplete.FilteredArrayAdapter;
+import com.tokenautocomplete.TokenCompleteTextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ContactHelper;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+public class EncryptKeyCompletionView extends TokenCompleteTextView {
+ public EncryptKeyCompletionView(Context context) {
+ super(context);
+ initView();
+ }
+
+ public EncryptKeyCompletionView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initView();
+ }
+
+ public EncryptKeyCompletionView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initView();
+ }
+
+ private void initView() {
+ swapCursor(null);
+ setPrefix(getContext().getString(R.string.label_to));
+ allowDuplicates(false);
+ }
+
+ @Override
+ protected View getViewForObject(Object object) {
+ if (object instanceof EncryptionKey) {
+ LayoutInflater l = (LayoutInflater) getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
+ View view = l.inflate(R.layout.recipient_box_entry, null);
+ ((TextView) view.findViewById(android.R.id.text1)).setText(((EncryptionKey) object).getPrimary());
+ setImageByKey((ImageView) view.findViewById(android.R.id.icon), (EncryptionKey) object);
+ return view;
+ }
+ return null;
+ }
+
+ private void setImageByKey(ImageView view, EncryptionKey key) {
+ Bitmap photo = ContactHelper.photoFromFingerprint(getContext().getContentResolver(), key.getFingerprint());
+
+ if (photo != null) {
+ view.setImageBitmap(photo);
+ } else {
+ view.setImageResource(R.drawable.ic_generic_man);
+ }
+ }
+
+ @Override
+ protected Object defaultObject(String completionText) {
+ // TODO: We could try to automagically download the key if it's unknown but a key id
+ /*if (completionText.startsWith("0x")) {
+
+ }*/
+ return null;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (getContext() instanceof FragmentActivity) {
+ ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ return new CursorLoader(getContext(), KeychainContract.KeyRings.buildUnifiedKeyRingsUri(),
+ new String[]{KeychainContract.KeyRings.HAS_ENCRYPT, KeychainContract.KeyRings.KEY_ID, KeychainContract.KeyRings.USER_ID, KeychainContract.KeyRings.FINGERPRINT},
+ null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ swapCursor(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ swapCursor(null);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onFocusChanged(boolean hasFocus, int direction, Rect previous) {
+ super.onFocusChanged(hasFocus, direction, previous);
+ if (hasFocus) {
+ ((InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
+ .showSoftInput(this, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+
+ public void swapCursor(Cursor cursor) {
+ if (cursor == null) {
+ setAdapter(new EncryptKeyAdapter(Collections.<EncryptionKey>emptyList()));
+ return;
+ }
+ ArrayList<EncryptionKey> keys = new ArrayList<EncryptionKey>();
+ while (cursor.moveToNext()) {
+ try {
+ if (cursor.getInt(cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.HAS_ENCRYPT)) != 0) {
+ EncryptionKey key = new EncryptionKey(cursor);
+ keys.add(key);
+ }
+ } catch (Exception e) {
+ Log.w(Constants.TAG, e);
+ return;
+ }
+ }
+ setAdapter(new EncryptKeyAdapter(keys));
+ }
+
+ public class EncryptionKey {
+ private String mUserIdFull;
+ private String[] mUserId;
+ private long mKeyId;
+ private String mFingerprint;
+
+ public EncryptionKey(String userId, long keyId, String fingerprint) {
+ this.mUserId = KeyRing.splitUserId(userId);
+ this.mUserIdFull = userId;
+ this.mKeyId = keyId;
+ this.mFingerprint = fingerprint;
+ }
+
+ public EncryptionKey(Cursor cursor) {
+ this(cursor.getString(cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.USER_ID)),
+ cursor.getLong(cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.KEY_ID)),
+ PgpKeyHelper.convertFingerprintToHex(
+ cursor.getBlob(cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.FINGERPRINT))));
+
+ }
+
+ public EncryptionKey(CachedPublicKeyRing ring) throws PgpGeneralException {
+ this(ring.getPrimaryUserId(), ring.extractOrGetMasterKeyId(),
+ PgpKeyHelper.convertFingerprintToHex(ring.getFingerprint()));
+ }
+
+ public String getUserId() {
+ return mUserIdFull;
+ }
+
+ public String getFingerprint() {
+ return mFingerprint;
+ }
+
+ public String getPrimary() {
+ if (mUserId[0] != null && mUserId[2] != null) {
+ return mUserId[0] + " (" + mUserId[2] + ")";
+ } else if (mUserId[0] != null) {
+ return mUserId[0];
+ } else {
+ return mUserId[1];
+ }
+ }
+
+ public String getSecondary() {
+ if (mUserId[0] != null) {
+ return mUserId[1];
+ } else {
+ return getKeyIdHex();
+ }
+ }
+
+ public String getTertiary() {
+ if (mUserId[0] != null) {
+ return getKeyIdHex();
+ } else {
+ return null;
+ }
+ }
+
+ public long getKeyId() {
+ return mKeyId;
+ }
+
+ public String getKeyIdHex() {
+ return PgpKeyHelper.convertKeyIdToHex(mKeyId);
+ }
+
+ public String getKeyIdHexShort() {
+ return PgpKeyHelper.convertKeyIdToHexShort(mKeyId);
+ }
+
+ @Override
+ public String toString() {
+ return Long.toString(mKeyId);
+ }
+ }
+
+ private class EncryptKeyAdapter extends FilteredArrayAdapter<EncryptionKey> {
+
+ public EncryptKeyAdapter(List<EncryptionKey> objs) {
+ super(EncryptKeyCompletionView.this.getContext(), 0, 0, objs);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater l = (LayoutInflater) getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
+ View view;
+ if (convertView != null) {
+ view = convertView;
+ } else {
+ view = l.inflate(R.layout.recipient_selection_list_entry, null);
+ }
+ ((TextView) view.findViewById(android.R.id.title)).setText(getItem(position).getPrimary());
+ ((TextView) view.findViewById(android.R.id.text1)).setText(getItem(position).getSecondary());
+ ((TextView) view.findViewById(android.R.id.text2)).setText(getItem(position).getTertiary());
+ setImageByKey((ImageView) view.findViewById(android.R.id.icon), getItem(position));
+ return view;
+ }
+
+ @Override
+ protected boolean keepObject(EncryptionKey obj, String mask) {
+ String m = mask.toLowerCase(Locale.ENGLISH);
+ return obj.getUserId().toLowerCase(Locale.ENGLISH).contains(m) ||
+ obj.getKeyIdHex().contains(m) ||
+ obj.getKeyIdHexShort().startsWith(m);
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
index a29c17d37..31e01a7fb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
@@ -24,9 +24,9 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
-import android.widget.ImageButton;
import org.sufficientlysecure.keychain.R;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
deleted file mode 100644
index c23b4c3ff..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * 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.widget;
-
-import android.annotation.TargetApi;
-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;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.DatePicker;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.TableLayout;
-import android.widget.TableRow;
-import android.widget.TextView;
-import android.widget.Button;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
-
-import java.text.DateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
- private UncachedSecretKey mKey;
-
- private EditorListener mEditorListener = null;
-
- private boolean mIsMasterKey;
- ImageButton mDeleteButton;
- TextView mAlgorithm;
- TextView mKeyId;
- TextView mCreationDate;
- Button mExpiryDateButton;
- Calendar mCreatedDate;
- Calendar mExpiryDate;
- Calendar mOriginalExpiryDate = null;
- CheckBox mChkCertify;
- CheckBox mChkSign;
- CheckBox mChkEncrypt;
- CheckBox mChkAuthenticate;
- int mUsage;
- int mOriginalUsage;
- boolean mIsNewKey;
-
- private CheckBox.OnCheckedChangeListener mCheckChanged = new CheckBox.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- }
- };
-
-
- private int mDatePickerResultCount = 0;
- private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
- new DatePickerDialog.OnDateSetListener() {
- public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
- // Note: Ignore results after the first one - android sends multiples.
- if (mDatePickerResultCount++ == 0) {
- GregorianCalendar date = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
- date.set(year, monthOfYear, dayOfMonth);
- if (mOriginalExpiryDate != null) {
- long numDays = (date.getTimeInMillis() / 86400000) -
- (mOriginalExpiryDate.getTimeInMillis() / 86400000);
- if (numDays == 0) {
- setExpiryDate(mOriginalExpiryDate);
- } else {
- setExpiryDate(date);
- }
- } else {
- setExpiryDate(date);
- }
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- }
- }
- };
-
- public KeyEditor(Context context) {
- super(context);
- }
-
- public KeyEditor(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mAlgorithm = (TextView) findViewById(R.id.algorithm);
- mKeyId = (TextView) findViewById(R.id.keyId);
- mCreationDate = (TextView) findViewById(R.id.creation);
- mExpiryDateButton = (Button) findViewById(R.id.expiry);
-
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(this);
- mChkCertify = (CheckBox) findViewById(R.id.chkCertify);
- mChkCertify.setOnCheckedChangeListener(mCheckChanged);
- mChkSign = (CheckBox) findViewById(R.id.chkSign);
- mChkSign.setOnCheckedChangeListener(mCheckChanged);
- mChkEncrypt = (CheckBox) findViewById(R.id.chkEncrypt);
- mChkEncrypt.setOnCheckedChangeListener(mCheckChanged);
- mChkAuthenticate = (CheckBox) findViewById(R.id.chkAuthenticate);
- mChkAuthenticate.setOnCheckedChangeListener(mCheckChanged);
-
- setExpiryDate(null);
-
- mExpiryDateButton.setOnClickListener(new OnClickListener() {
- @TargetApi(11)
- public void onClick(View v) {
- Calendar expiryDate = mExpiryDate;
- if (expiryDate == null) {
- expiryDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- }
- /*
- * 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, expiryDate.get(Calendar.YEAR), expiryDate.get(Calendar.MONTH),
- expiryDate.get(Calendar.DAY_OF_MONTH));
- mDatePickerResultCount = 0;
- dialog.setCancelable(true);
- dialog.setButton(Dialog.BUTTON_NEGATIVE,
- getContext().getString(R.string.btn_no_date),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // Note: Ignore results after the first one - android sends multiples.
- if (mDatePickerResultCount++ == 0) {
- setExpiryDate(null);
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- }
- }
- });
-
- // 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) {
-
- // will crash with IllegalArgumentException if we set a min date
- // that is not before expiry
- if (mCreatedDate != null && mCreatedDate.before(expiryDate)) {
- dialog.getDatePicker()
- .setMinDate(
- mCreatedDate.getTime().getTime() + DateUtils.DAY_IN_MILLIS);
- } else {
- // When created date isn't available
- dialog.getDatePicker().setMinDate(expiryDate.getTime().getTime() + DateUtils.DAY_IN_MILLIS);
- }
- }
-
- dialog.show();
- }
- });
-
- super.onFinishInflate();
- }
-
- public void setCanBeEdited(boolean canBeEdited) {
- if (!canBeEdited) {
- mDeleteButton.setVisibility(View.INVISIBLE);
- mExpiryDateButton.setEnabled(false);
- mChkSign.setEnabled(false); //certify is always disabled
- mChkEncrypt.setEnabled(false);
- mChkAuthenticate.setEnabled(false);
- }
- }
-
- public void setValue(UncachedSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
- mKey = key;
-
- mIsMasterKey = isMasterKey;
- if (mIsMasterKey) {
- mDeleteButton.setVisibility(View.INVISIBLE);
- }
-
- mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(getContext(), key.getAlgorithm()));
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyId());
- mKeyId.setText(keyIdStr);
-
- boolean isElGamalKey = (key.isElGamalEncrypt());
- boolean isDSAKey = (key.isDSA());
- if (isElGamalKey) {
- mChkSign.setVisibility(View.INVISIBLE);
- TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
- TableRow row = (TableRow) findViewById(R.id.row_sign);
- table.removeView(row);
- }
- if (isDSAKey) {
- mChkEncrypt.setVisibility(View.INVISIBLE);
- TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
- TableRow row = (TableRow) findViewById(R.id.row_encrypt);
- table.removeView(row);
- }
- if (!mIsMasterKey) {
- mChkCertify.setVisibility(View.INVISIBLE);
- TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
- TableRow row = (TableRow) findViewById(R.id.row_certify);
- table.removeView(row);
- } else {
- TextView mLabelUsage2 = (TextView) findViewById(R.id.label_usage2);
- mLabelUsage2.setVisibility(View.INVISIBLE);
- }
-
- mIsNewKey = isNewKey;
- if (isNewKey) {
- mUsage = usage;
- mChkCertify.setChecked(
- (usage & UncachedSecretKey.CERTIFY_OTHER) == UncachedSecretKey.CERTIFY_OTHER);
- mChkSign.setChecked(
- (usage & UncachedSecretKey.SIGN_DATA) == UncachedSecretKey.SIGN_DATA);
- mChkEncrypt.setChecked(
- ((usage & UncachedSecretKey.ENCRYPT_COMMS) == UncachedSecretKey.ENCRYPT_COMMS) ||
- ((usage & UncachedSecretKey.ENCRYPT_STORAGE) == UncachedSecretKey.ENCRYPT_STORAGE));
- mChkAuthenticate.setChecked(
- (usage & UncachedSecretKey.AUTHENTICATION) == UncachedSecretKey.AUTHENTICATION);
- } else {
- mUsage = key.getKeyUsage();
- mOriginalUsage = mUsage;
- if (key.isMasterKey()) {
- mChkCertify.setChecked(key.canCertify());
- }
- mChkSign.setChecked(key.canSign());
- mChkEncrypt.setChecked(key.canEncrypt());
- mChkAuthenticate.setChecked(key.canAuthenticate());
- }
-
- {
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- cal.setTime(key.getCreationTime());
- setCreatedDate(cal);
- }
-
- Date expiryDate = key.getExpiryTime();
- if (expiryDate == null) {
- setExpiryDate(null);
- } else {
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- cal.setTime(expiryDate);
- setExpiryDate(cal);
- mOriginalExpiryDate = cal;
- }
-
- }
-
- public UncachedSecretKey getValue() {
- return mKey;
- }
-
- public void onClick(View v) {
- final ViewGroup parent = (ViewGroup) getParent();
- if (v == mDeleteButton) {
- parent.removeView(this);
- if (mEditorListener != null) {
- mEditorListener.onDeleted(this, mIsNewKey);
- }
- }
- }
-
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-
- private void setCreatedDate(Calendar date) {
- mCreatedDate = date;
- if (date == null) {
- mCreationDate.setText(getContext().getString(R.string.none));
- } else {
- mCreationDate.setText(DateFormat.getDateInstance().format(date.getTime()));
- }
- }
-
- private void setExpiryDate(Calendar date) {
- mExpiryDate = date;
- if (date == null) {
- mExpiryDateButton.setText(getContext().getString(R.string.none));
- } else {
- mExpiryDateButton.setText(DateFormat.getDateInstance().format(date.getTime()));
- }
- }
-
- public Calendar getExpiryDate() {
- return mExpiryDate;
- }
-
- public int getUsage() {
- mUsage = (mUsage & ~UncachedSecretKey.CERTIFY_OTHER) |
- (mChkCertify.isChecked() ? UncachedSecretKey.CERTIFY_OTHER : 0);
- mUsage = (mUsage & ~UncachedSecretKey.SIGN_DATA) |
- (mChkSign.isChecked() ? UncachedSecretKey.SIGN_DATA : 0);
- mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_COMMS) |
- (mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_COMMS : 0);
- mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_STORAGE) |
- (mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_STORAGE : 0);
- mUsage = (mUsage & ~UncachedSecretKey.AUTHENTICATION) |
- (mChkAuthenticate.isChecked() ? UncachedSecretKey.AUTHENTICATION : 0);
-
- return mUsage;
- }
-
- public boolean needsSaving() {
- if (mIsNewKey) {
- return true;
- }
-
- boolean retval = (getUsage() != mOriginalUsage);
-
- boolean dateChanged;
- boolean mOEDNull = (mOriginalExpiryDate == null);
- boolean mEDNull = (mExpiryDate == null);
- if (mOEDNull != mEDNull) {
- dateChanged = true;
- } else {
- if (mOEDNull) {
- //both null, no change
- dateChanged = false;
- } else {
- dateChanged = ((mExpiryDate.compareTo(mOriginalExpiryDate)) != 0);
- }
- }
- retval |= dateChanged;
-
- return retval;
- }
-
- public boolean getIsNewKey() {
- return mIsNewKey;
- }
-}
-
-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));
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java
new file mode 100644
index 000000000..a48d2a026
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java
@@ -0,0 +1,62 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class NoSwipeWrapContentViewPager extends android.support.v4.view.ViewPager {
+ public NoSwipeWrapContentViewPager(Context context) {
+ super(context);
+ }
+
+ public NoSwipeWrapContentViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ int height;
+ View child = getChildAt(getCurrentItem());
+ if (child != null) {
+ child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ height = child.getMeasuredHeight();
+ } else {
+ height = 0;
+ }
+
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent arg0) {
+ // Never allow swiping to switch between pages
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ // Never allow swiping to switch between pages
+ return false;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
deleted file mode 100644
index cd5671801..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * 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.widget;
-
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.support.v7.app.ActionBarActivity;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.ImageButton;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
-import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
-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;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Vector;
-
-public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Editor {
- private LayoutInflater mInflater;
- private ImageButton mPlusButton;
- private ViewGroup mEditors;
- private TextView mTitle;
- private int mType = 0;
- private EditorListener mEditorListener = null;
-
- private Choice mNewKeyAlgorithmChoice;
- private int mNewKeySize;
- private boolean mOldItemDeleted = false;
- private ArrayList<String> mDeletedIDs = new ArrayList<String>();
- private ArrayList<UncachedSecretKey> mDeletedKeys = new ArrayList<UncachedSecretKey>();
- private boolean mCanBeEdited = true;
-
- private ActionBarActivity mActivity;
-
- private ProgressDialogFragment mGeneratingDialog;
-
- public static final int TYPE_USER_ID = 1;
- public static final int TYPE_KEY = 2;
-
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-
- public SectionView(Context context) {
- super(context);
- mActivity = (ActionBarActivity) context;
- }
-
- public SectionView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mActivity = (ActionBarActivity) context;
- }
-
- public ViewGroup getEditors() {
- return mEditors;
- }
-
- public void setType(int type) {
- mType = type;
- switch (type) {
- case TYPE_USER_ID: {
- mTitle.setText(R.string.section_user_ids);
- break;
- }
-
- case TYPE_KEY: {
- mTitle.setText(R.string.section_keys);
- break;
- }
-
- default: {
- break;
- }
- }
- }
-
- public void setCanBeEdited(boolean canBeEdited) {
- mCanBeEdited = canBeEdited;
- if (!mCanBeEdited) {
- mPlusButton.setVisibility(View.INVISIBLE);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onFinishInflate() {
- mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mPlusButton = (ImageButton) findViewById(R.id.plusbutton);
- mPlusButton.setOnClickListener(this);
-
- mEditors = (ViewGroup) findViewById(R.id.editors);
- mTitle = (TextView) findViewById(R.id.title);
-
- updateEditorsVisible();
- super.onFinishInflate();
- }
-
- /**
- * {@inheritDoc}
- */
- public void onDeleted(Editor editor, boolean wasNewItem) {
- mOldItemDeleted |= !wasNewItem;
- if (mOldItemDeleted) {
- if (mType == TYPE_USER_ID) {
- mDeletedIDs.add(((UserIdEditor) editor).getOriginalID());
- } else if (mType == TYPE_KEY) {
- mDeletedKeys.add(((KeyEditor) editor).getValue());
- }
- }
- this.updateEditorsVisible();
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- }
-
- @Override
- public void onEdited() {
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- }
-
- protected void updateEditorsVisible() {
- final boolean hasChildren = mEditors.getChildCount() > 0;
- mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
- }
-
- public boolean needsSaving() {
- //check each view for needs saving, take account of deleted items
- boolean ret = mOldItemDeleted;
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- Editor editor = (Editor) mEditors.getChildAt(i);
- ret |= editor.needsSaving();
- if (mType == TYPE_USER_ID) {
- ret |= ((UserIdEditor) editor).primarySwapped();
- }
- }
- return ret;
- }
-
- public boolean primaryChanged() {
- boolean ret = false;
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- Editor editor = (Editor) mEditors.getChildAt(i);
- if (mType == TYPE_USER_ID) {
- ret |= ((UserIdEditor) editor).primarySwapped();
- }
- }
- return ret;
- }
-
- public String getOriginalPrimaryID() {
- //NB: this will have to change when we change how Primary IDs are chosen, and so we need to be
- // careful about where Master key capabilities are stored... multiple primaries and
- // revoked ones make this harder than the simple case we are continuing to assume here
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- Editor editor = (Editor) mEditors.getChildAt(i);
- if (mType == TYPE_USER_ID) {
- if (((UserIdEditor) editor).getIsOriginallyMainUserID()) {
- return ((UserIdEditor) editor).getOriginalID();
- }
- }
- }
- return null;
- }
-
- public ArrayList<String> getOriginalIDs() {
- ArrayList<String> orig = new ArrayList<String>();
- if (mType == TYPE_USER_ID) {
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- UserIdEditor editor = (UserIdEditor) mEditors.getChildAt(i);
- if (editor.isMainUserId()) {
- orig.add(0, editor.getOriginalID());
- } else {
- orig.add(editor.getOriginalID());
- }
- }
- return orig;
- } else {
- return null;
- }
- }
-
- public ArrayList<String> getDeletedIDs() {
- return mDeletedIDs;
- }
-
- public ArrayList<UncachedSecretKey> getDeletedKeys() {
- return mDeletedKeys;
- }
-
- public List<Boolean> getNeedsSavingArray() {
- ArrayList<Boolean> mList = new ArrayList<Boolean>();
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- Editor editor = (Editor) mEditors.getChildAt(i);
- mList.add(editor.needsSaving());
- }
- return mList;
- }
-
- public List<Boolean> getNewIDFlags() {
- ArrayList<Boolean> mList = new ArrayList<Boolean>();
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- UserIdEditor editor = (UserIdEditor) mEditors.getChildAt(i);
- if (editor.isMainUserId()) {
- mList.add(0, editor.getIsNewID());
- } else {
- mList.add(editor.getIsNewID());
- }
- }
- return mList;
- }
-
- public List<Boolean> getNewKeysArray() {
- ArrayList<Boolean> mList = new ArrayList<Boolean>();
- if (mType == TYPE_KEY) {
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- KeyEditor editor = (KeyEditor) mEditors.getChildAt(i);
- mList.add(editor.getIsNewKey());
- }
- }
- return mList;
- }
-
- /**
- * {@inheritDoc}
- */
- public void onClick(View v) {
- if (mCanBeEdited) {
- switch (mType) {
- case TYPE_USER_ID: {
- UserIdEditor view = (UserIdEditor) mInflater.inflate(
- R.layout.edit_key_user_id_item, mEditors, false);
- view.setEditorListener(this);
- view.setValue("", mEditors.getChildCount() == 0, true);
- mEditors.addView(view);
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- break;
- }
-
- case TYPE_KEY: {
- CreateKeyDialogFragment mCreateKeyDialogFragment =
- CreateKeyDialogFragment.newInstance(mEditors.getChildCount());
- mCreateKeyDialogFragment
- .setOnAlgorithmSelectedListener(
- new CreateKeyDialogFragment.OnAlgorithmSelectedListener() {
- @Override
- public void onAlgorithmSelected(Choice algorithmChoice, int keySize) {
- mNewKeyAlgorithmChoice = algorithmChoice;
- mNewKeySize = keySize;
- createKey();
- }
- });
- mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog");
- break;
- }
-
- default: {
- break;
- }
- }
- this.updateEditorsVisible();
- }
- }
-
- public void setUserIds(Vector<String> list) {
- if (mType != TYPE_USER_ID) {
- return;
- }
-
- mEditors.removeAllViews();
- for (String userId : list) {
- UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item,
- mEditors, false);
- view.setEditorListener(this);
- view.setValue(userId, mEditors.getChildCount() == 0, false);
- view.setCanBeEdited(mCanBeEdited);
- mEditors.addView(view);
- }
-
- this.updateEditorsVisible();
- }
-
- public void setKeys(Vector<UncachedSecretKey> list, Vector<Integer> usages, boolean newKeys) {
- if (mType != TYPE_KEY) {
- return;
- }
-
- mEditors.removeAllViews();
-
- // go through all keys and set view based on them
- for (int i = 0; i < list.size(); i++) {
- KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors,
- false);
- view.setEditorListener(this);
- boolean isMasterKey = (mEditors.getChildCount() == 0);
- view.setValue(list.get(i), isMasterKey, usages.get(i), newKeys);
- view.setCanBeEdited(mCanBeEdited);
- mEditors.addView(view);
- }
-
- this.updateEditorsVisible();
- }
-
- private void createKey() {
-
- // fill values for this action
- Boolean isMasterKey;
-
- String passphrase;
- if (mEditors.getChildCount() > 0) {
- UncachedSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
- passphrase = PassphraseCacheService
- .getCachedPassphrase(mActivity, masterKey.getKeyId());
- isMasterKey = false;
- } else {
- passphrase = "";
- isMasterKey = true;
- }
- /*
- data.putBoolean(KeychainIntentService.GENERATE_KEY_MASTER_KEY, isMasterKey);
- data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase);
- data.putInt(KeychainIntentService.GENERATE_KEY_ALGORITHM, mNewKeyAlgorithmChoice.getId());
- data.putInt(KeychainIntentService.GENERATE_KEY_KEY_SIZE, mNewKeySize);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // show progress dialog
- mGeneratingDialog = ProgressDialogFragment.newInstance(
- getResources().getQuantityString(R.plurals.progress_generating, 1),
- ProgressDialog.STYLE_SPINNER,
- true,
- new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- mActivity.stopService(intent);
- }
- });
-
- // Message is received after generating is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity,
- mGeneratingDialog) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get new key from data bundle returned from service
- Bundle data = message.getDataAsStringList();
- UncachedSecretKey newKey = PgpConversionHelper
- .BytesToPGPSecretKey(data
- .getByteArray(KeychainIntentService.RESULT_NEW_KEY));
- addGeneratedKeyToView(newKey);
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- mGeneratingDialog.show(mActivity.getSupportFragmentManager(), "dialog");
-
- // start service with intent
- mActivity.startService(intent);
- */
-
- }
-
- private void addGeneratedKeyToView(UncachedSecretKey newKey) {
- // add view with new key
- KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
- mEditors, false);
- view.setEditorListener(SectionView.this);
- int usage = 0;
- if (mEditors.getChildCount() == 0) {
- usage = UncachedSecretKey.CERTIFY_OTHER;
- }
- view.setValue(newKey, newKey.isMasterKey(), usage, true);
- mEditors.addView(view);
- SectionView.this.updateEditorsVisible();
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
deleted file mode 100644
index f50d2adc6..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * 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.widget;
-
-import android.content.Context;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.util.Patterns;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.AutoCompleteTextView;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-import android.widget.ImageButton;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ContactHelper;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
-
-import java.util.regex.Matcher;
-
-public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
- private EditorListener mEditorListener = null;
-
- private ImageButton mDeleteButton;
- private RadioButton mIsMainUserId;
- private String mOriginalID;
- private EditText mName;
- private String mOriginalName;
- private AutoCompleteTextView mEmail;
- private String mOriginalEmail;
- private EditText mComment;
- private String mOriginalComment;
- private boolean mOriginallyMainUserID;
- private boolean mIsNewId;
-
- public void setCanBeEdited(boolean canBeEdited) {
- if (!canBeEdited) {
- mDeleteButton.setVisibility(View.INVISIBLE);
- mName.setEnabled(false);
- mIsMainUserId.setEnabled(false);
- mEmail.setEnabled(false);
- mComment.setEnabled(false);
- }
- }
-
- public static class InvalidEmailException extends Exception {
- static final long serialVersionUID = 0xf812773345L;
-
- public InvalidEmailException(String message) {
- super(message);
- }
- }
-
- public UserIdEditor(Context context) {
- super(context);
- }
-
- public UserIdEditor(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- TextWatcher mTextWatcher = new TextWatcher() {
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- }
- };
-
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(this);
- mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId);
- mIsMainUserId.setOnClickListener(this);
-
- mName = (EditText) findViewById(R.id.name);
- mName.addTextChangedListener(mTextWatcher);
- mEmail = (AutoCompleteTextView) findViewById(R.id.email);
- mComment = (EditText) findViewById(R.id.user_id_item_comment);
- mComment.addTextChangedListener(mTextWatcher);
-
-
- mEmail.setThreshold(1); // Start working from first character
- mEmail.setAdapter(
- new ArrayAdapter<String>
- (this.getContext(), android.R.layout.simple_dropdown_item_1line,
- ContactHelper.getPossibleUserEmails(getContext())
- ));
- mEmail.addTextChangedListener(new TextWatcher(){
- @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 email = editable.toString();
- if (email.length() > 0) {
- Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
- if (emailMatcher.matches()) {
- mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_ok, 0);
- } else {
- mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_bad, 0);
- }
- } else {
- // remove drawable if email is empty
- mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
- }
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- }
- });
-
- super.onFinishInflate();
- }
-
- public void setValue(String userId, boolean isMainID, boolean isNewId) {
-
- mName.setText("");
- mOriginalName = "";
- mComment.setText("");
- mOriginalComment = "";
- mEmail.setText("");
- mOriginalEmail = "";
- mIsNewId = isNewId;
- mOriginalID = userId;
-
- String[] result = KeyRing.splitUserId(userId);
- if (result[0] != null) {
- mName.setText(result[0]);
- mOriginalName = result[0];
- }
- if (result[1] != null) {
- mEmail.setText(result[1]);
- mOriginalEmail = result[1];
- }
- if (result[2] != null) {
- mComment.setText(result[2]);
- mOriginalComment = result[2];
- }
-
- mOriginallyMainUserID = isMainID;
- setIsMainUserId(isMainID);
- }
-
- public String getValue() {
- String name = ("" + mName.getText()).trim();
- String email = ("" + mEmail.getText()).trim();
- String comment = ("" + mComment.getText()).trim();
-
- String userId = name;
- if (comment.length() > 0) {
- userId += " (" + comment + ")";
- }
- if (email.length() > 0) {
- userId += " <" + email + ">";
- }
-
- if (userId.equals("")) {
- // ok, empty one...
- return userId;
- }
- //TODO: check gpg accepts an entirely empty ID packet. specs say this is allowed
- return userId;
- }
-
- public void onClick(View v) {
- final ViewGroup parent = (ViewGroup) getParent();
- if (v == mDeleteButton) {
- boolean wasMainUserId = mIsMainUserId.isChecked();
- parent.removeView(this);
- if (mEditorListener != null) {
- mEditorListener.onDeleted(this, mIsNewId);
- }
- if (wasMainUserId && parent.getChildCount() > 0) {
- UserIdEditor editor = (UserIdEditor) parent.getChildAt(0);
- editor.setIsMainUserId(true);
- }
- } else if (v == mIsMainUserId) {
- for (int i = 0; i < parent.getChildCount(); ++i) {
- UserIdEditor editor = (UserIdEditor) parent.getChildAt(i);
- if (editor == this) {
- editor.setIsMainUserId(true);
- } else {
- editor.setIsMainUserId(false);
- }
- }
- if (mEditorListener != null) {
- mEditorListener.onEdited();
- }
- }
- }
-
- public void setIsMainUserId(boolean value) {
- mIsMainUserId.setChecked(value);
- }
-
- public boolean isMainUserId() {
- return mIsMainUserId.isChecked();
- }
-
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-
- @Override
- public boolean needsSaving() {
- boolean retval = false; //(mOriginallyMainUserID != isMainUserId());
- retval |= !(mOriginalName.equals(("" + mName.getText()).trim()));
- retval |= !(mOriginalEmail.equals(("" + mEmail.getText()).trim()));
- retval |= !(mOriginalComment.equals(("" + mComment.getText()).trim()));
- retval |= mIsNewId;
- return retval;
- }
-
- public boolean getIsOriginallyMainUserID() {
- return mOriginallyMainUserID;
- }
-
- public boolean primarySwapped() {
- return (mOriginallyMainUserID != isMainUserId());
- }
-
- public String getOriginalID() {
- return mOriginalID;
- }
-
- public boolean getIsNewID() { return mIsNewId; }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
index 2499f2571..9acc7a73b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.util;
import android.annotation.SuppressLint;
import android.app.Activity;
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
@@ -57,13 +58,13 @@ public class AlgorithmNames {
mHashNames.put(HashAlgorithmTags.SHA384, "SHA-384");
mHashNames.put(HashAlgorithmTags.SHA512, "SHA-512");
- mCompressionNames.put(Constants.choice.compression.none, mActivity.getString(R.string.choice_none)
+ mCompressionNames.put(CompressionAlgorithmTags.UNCOMPRESSED, mActivity.getString(R.string.choice_none)
+ " (" + mActivity.getString(R.string.compression_fast) + ")");
- mCompressionNames.put(Constants.choice.compression.zip,
+ mCompressionNames.put(CompressionAlgorithmTags.ZIP,
"ZIP (" + mActivity.getString(R.string.compression_fast) + ")");
- mCompressionNames.put(Constants.choice.compression.zlib,
+ mCompressionNames.put(CompressionAlgorithmTags.ZLIB,
"ZLIB (" + mActivity.getString(R.string.compression_fast) + ")");
- mCompressionNames.put(Constants.choice.compression.bzip2,
+ mCompressionNames.put(CompressionAlgorithmTags.BZIP2,
"BZIP2 (" + mActivity.getString(R.string.compression_very_slow) + ")");
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/DatabaseUtil.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/DatabaseUtil.java
new file mode 100644
index 000000000..c18e5cabd
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/DatabaseUtil.java
@@ -0,0 +1,36 @@
+package org.sufficientlysecure.keychain.util;
+
+import android.text.TextUtils;
+
+/**
+ * Shamelessly copied from android.database.DatabaseUtils
+ */
+public class DatabaseUtil {
+ /**
+ * Concatenates two SQL WHERE clauses, handling empty or null values.
+ */
+ public static String concatenateWhere(String a, String b) {
+ if (TextUtils.isEmpty(a)) {
+ return b;
+ }
+ if (TextUtils.isEmpty(b)) {
+ return a;
+ }
+
+ return "(" + a + ") AND (" + b + ")";
+ }
+
+ /**
+ * Appends one set of selection args to another. This is useful when adding a selection
+ * argument to a user provided set.
+ */
+ public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {
+ if (originalValues == null || originalValues.length == 0) {
+ return newValues;
+ }
+ String[] result = new String[originalValues.length + newValues.length ];
+ System.arraycopy(originalValues, 0, result, 0, originalValues.length);
+ System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
+ return result;
+ }
+}