aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2014-06-19 17:07:52 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2014-06-19 17:07:52 +0200
commit313e571a61e22a7152e32366d747114233d25b6e (patch)
treed250292332dce208760847f150c4e627607d8028
parent48c96184dcf2837f9be2e5bdb116bedd86098eb3 (diff)
parent58706425d62e40c0ecc9164a8fee089ca471e1fe (diff)
downloadopen-keychain-313e571a61e22a7152e32366d747114233d25b6e.tar.gz
open-keychain-313e571a61e22a7152e32366d747114233d25b6e.tar.bz2
open-keychain-313e571a61e22a7152e32366d747114233d25b6e.zip
Merge pull request #664 from mar-v-in/improve-file
Add Document/Storage API support
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java277
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java88
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java92
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java58
9 files changed, 349 insertions, 232 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index c769da421..319ac2873 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain;
+import android.os.Build;
import android.os.Environment;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
@@ -48,6 +49,8 @@ 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 final class Path {
public static final String APP_DIR = Environment.getExternalStorageDirectory()
+ "/OpenKeychain";
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 d7d73cf3d..e0c94b947 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
@@ -17,12 +17,14 @@
package org.sufficientlysecure.keychain.helper;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Environment;
import android.support.v4.app.Fragment;
import android.widget.Toast;
@@ -82,6 +84,39 @@ public class FileHelper {
}
}
+
+ /**
+ * 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 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) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setData(last);
+ intent.setType(mimeType);
+ 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
+ */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ public static void saveDocument(Fragment fragment, Uri last, String mimeType, int requestCode) {
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setData(last);
+ intent.setType(mimeType);
+ 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);
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 7e2cdcd15..e1514b16f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -104,9 +104,11 @@ public class KeychainIntentService extends IntentService
// encrypt, decrypt, import export
public static final String TARGET = "target";
+ public static final String SOURCE = "source";
// possible targets:
- public static final int TARGET_BYTES = 1;
- public static final int TARGET_URI = 2;
+ 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;
// encrypt
public static final String ENCRYPT_SIGNATURE_KEY_ID = "secret_key_id";
@@ -115,7 +117,9 @@ public class KeychainIntentService extends IntentService
public static final String ENCRYPT_COMPRESSION_ID = "compression_id";
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_OUTPUT_FILE = "output_file";
+ public static final String ENCRYPT_OUTPUT_URI = "output_uri";
public static final String ENCRYPT_SYMMETRIC_PASSPHRASE = "passphrase";
// decrypt/verify
@@ -214,7 +218,7 @@ public class KeychainIntentService extends IntentService
if (ACTION_ENCRYPT_SIGN.equals(action)) {
try {
/* Input */
- int target = data.getInt(TARGET);
+ int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
long signatureKeyId = data.getLong(ENCRYPT_SIGNATURE_KEY_ID);
String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE);
@@ -222,71 +226,8 @@ 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);
- InputStream inStream;
- long inLength;
- InputData inputData;
- OutputStream outStream;
-// String streamFilename = null;
- switch (target) {
- case TARGET_BYTES: /* encrypting bytes directly */
- byte[] bytes = data.getByteArray(ENCRYPT_MESSAGE_BYTES);
-
- inStream = new ByteArrayInputStream(bytes);
- inLength = bytes.length;
-
- inputData = new InputData(inStream, inLength);
- outStream = new ByteArrayOutputStream();
-
- break;
- case TARGET_URI: /* encrypting file */
- String inputFile = data.getString(ENCRYPT_INPUT_FILE);
- String outputFile = data.getString(ENCRYPT_OUTPUT_FILE);
-
- // check if storage is ready
- if (!FileHelper.isStorageMounted(inputFile)
- || !FileHelper.isStorageMounted(outputFile)) {
- throw new PgpGeneralException(
- getString(R.string.error_external_storage_not_ready));
- }
-
- inStream = new FileInputStream(inputFile);
- File file = new File(inputFile);
- inLength = file.length();
- inputData = new InputData(inStream, inLength);
-
- outStream = new FileOutputStream(outputFile);
-
- break;
-
- // TODO: not used currently
-// case TARGET_STREAM: /* Encrypting stream from content uri */
-// Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
-//
-// // InputStream
-// InputStream in = getContentResolver().openInputStream(providerUri);
-// inLength = PgpHelper.getLengthOfStream(in);
-// inputData = new InputData(in, inLength);
-//
-// // OutputStream
-// try {
-// while (true) {
-// streamFilename = PgpHelper.generateRandomFilename(32);
-// if (streamFilename == null) {
-// throw new PgpGeneralException("couldn't generate random file name");
-// }
-// openFileInput(streamFilename).close();
-// }
-// } catch (FileNotFoundException e) {
-// // found a name that isn't used yet
-// }
-// outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
-//
-// break;
-
- default:
- throw new PgpGeneralException("No target choosen!");
-
- }
+ InputData inputData = createEncryptInputData(data);
+ OutputStream outStream = createCryptOutputStream(data);
/* Operation */
PgpSignEncrypt.Builder builder =
@@ -311,7 +252,7 @@ public class KeychainIntentService extends IntentService
PassphraseCacheService.getCachedPassphrase(this, signatureKeyId));
// this assumes that the bytes are cleartext (valid for current implementation!)
- if (target == TARGET_BYTES) {
+ if (source == IO_BYTES) {
builder.setCleartextInput(true);
}
@@ -322,24 +263,7 @@ public class KeychainIntentService extends IntentService
/* Output */
Bundle resultData = new Bundle();
-
- switch (target) {
- case TARGET_BYTES:
- byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
-
- resultData.putByteArray(RESULT_BYTES, output);
-
- break;
- case TARGET_URI:
- // nothing, file was written, just send okay
-
- break;
-// case TARGET_STREAM:
-// String uri = DataStream.buildDataStreamUri(streamFilename).toString();
-// resultData.putString(RESULT_URI, uri);
-//
-// break;
- }
+ finalizeEncryptOutputStream(data, resultData, outStream);
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -350,78 +274,10 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_DECRYPT_VERIFY.equals(action)) {
try {
/* Input */
- int target = data.getInt(TARGET);
-
- byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES);
String passphrase = data.getString(DECRYPT_PASSPHRASE);
- InputStream inStream;
- long inLength;
- InputData inputData;
- OutputStream outStream;
- String streamFilename = null;
- switch (target) {
- case TARGET_BYTES: /* decrypting bytes directly */
- inStream = new ByteArrayInputStream(bytes);
- inLength = bytes.length;
-
- inputData = new InputData(inStream, inLength);
- outStream = new ByteArrayOutputStream();
-
- break;
-
- case TARGET_URI: /* decrypting file */
- String inputFile = data.getString(ENCRYPT_INPUT_FILE);
- String outputFile = data.getString(ENCRYPT_OUTPUT_FILE);
-
- // check if storage is ready
- if (!FileHelper.isStorageMounted(inputFile)
- || !FileHelper.isStorageMounted(outputFile)) {
- throw new PgpGeneralException(
- getString(R.string.error_external_storage_not_ready));
- }
-
- // InputStream
- inLength = -1;
- inStream = new FileInputStream(inputFile);
- File file = new File(inputFile);
- inLength = file.length();
- inputData = new InputData(inStream, inLength);
-
- // OutputStream
- outStream = new FileOutputStream(outputFile);
-
- break;
-
- // TODO: not used, maybe contains code useful for new decrypt method for files?
-// case TARGET_STREAM: /* decrypting stream from content uri */
-// Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
-//
-// // InputStream
-// InputStream in = getContentResolver().openInputStream(providerUri);
-// inLength = PgpHelper.getLengthOfStream(in);
-// inputData = new InputData(in, inLength);
-//
-// // OutputStream
-// try {
-// while (true) {
-// streamFilename = PgpHelper.generateRandomFilename(32);
-// if (streamFilename == null) {
-// throw new PgpGeneralException("couldn't generate random file name");
-// }
-// openFileInput(streamFilename).close();
-// }
-// } catch (FileNotFoundException e) {
-// // found a name that isn't used yet
-// }
-// outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
-//
-// break;
-
- default:
- throw new PgpGeneralException("No target choosen!");
-
- }
+ InputData inputData = createDecryptInputData(data);
+ OutputStream outStream = createCryptOutputStream(data);
/* Operation */
@@ -452,21 +308,7 @@ public class KeychainIntentService extends IntentService
/* Output */
- switch (target) {
- case TARGET_BYTES:
- byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
- resultData.putByteArray(RESULT_DECRYPTED_BYTES, output);
- break;
- case TARGET_URI:
- // nothing, file was written, just send okay and verification bundle
-
- break;
-// case TARGET_STREAM:
-// String uri = DataStream.buildDataStreamUri(streamFilename).toString();
-// resultData.putString(RESULT_URI, uri);
-//
-// break;
- }
+ finalizeDecryptOutputStream(data, resultData, outStream);
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -805,4 +647,95 @@ public class KeychainIntentService extends IntentService
public boolean hasServiceStopped() {
return mIsCanceled;
}
+
+ private InputData createDecryptInputData(Bundle data) throws IOException, PgpGeneralException {
+ return createCryptInputData(data, DECRYPT_CIPHERTEXT_BYTES);
+ }
+
+ private InputData createEncryptInputData(Bundle data) throws IOException, PgpGeneralException {
+ return createCryptInputData(data, ENCRYPT_MESSAGE_BYTES);
+ }
+
+ private InputData createCryptInputData(Bundle data, String bytesName) throws PgpGeneralException, IOException {
+ int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
+ switch (source) {
+ case IO_BYTES: /* encrypting bytes directly */
+ 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);
+
+ default:
+ throw new PgpGeneralException("No target choosen!");
+ }
+ }
+
+ private OutputStream createCryptOutputStream(Bundle data) throws PgpGeneralException, FileNotFoundException {
+ int target = data.getInt(TARGET);
+ switch (target) {
+ 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);
+
+ default:
+ throw new PgpGeneralException("No target choosen!");
+ }
+ }
+
+ private void finalizeEncryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream) {
+ finalizeCryptOutputStream(data, resultData, outStream, RESULT_BYTES);
+ }
+
+ private void finalizeDecryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream) {
+ finalizeCryptOutputStream(data, resultData, outStream, RESULT_DECRYPTED_BYTES);
+ }
+
+ private void finalizeCryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream, String bytesName) {
+ int target = data.getInt(TARGET);
+ switch (target) {
+ case IO_BYTES:
+ 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:
+ // nothing, output was written, just send okay and verification bundle
+
+ break;
+ }
+ }
}
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 7fdab7bdd..28a465436 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
@@ -20,10 +20,13 @@ 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.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;
@@ -58,7 +61,9 @@ public class DecryptFileFragment extends DecryptFragment {
private View mDecryptButton;
private String mInputFilename = null;
+ private Uri mInputUri = null;
private String mOutputFilename = null;
+ private Uri mOutputUri = null;
private FileDialogFragment mFileDialog;
@@ -75,8 +80,12 @@ public class DecryptFileFragment extends DecryptFragment {
mDecryptButton = view.findViewById(R.id.decrypt_file_action_decrypt);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
- FileHelper.openFile(DecryptFileFragment.this, mFilename.getText().toString(), "*/*",
- RESULT_CODE_FILE);
+ if (Constants.KITKAT) {
+ FileHelper.openDocument(DecryptFileFragment.this, mInputUri, "*/*", RESULT_CODE_FILE);
+ } else {
+ FileHelper.openFile(DecryptFileFragment.this, mFilename.getText().toString(), "*/*",
+ RESULT_CODE_FILE);
+ }
}
});
mDecryptButton.setOnClickListener(new View.OnClickListener() {
@@ -99,20 +108,24 @@ public class DecryptFileFragment extends DecryptFragment {
}
}
- private void guessOutputFilename() {
- mInputFilename = mFilename.getText().toString();
+ 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);
}
- mOutputFilename = Constants.Path.APP_DIR + "/" + filename;
+ return Constants.Path.APP_DIR + "/" + filename;
}
private void decryptAction() {
String currentFilename = mFilename.getText().toString();
if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
- guessOutputFilename();
+ mInputUri = null;
+ mInputFilename = mFilename.getText().toString();
+ }
+
+ if (mInputUri == null) {
+ mOutputFilename = guessOutputFilename();
}
if (mInputFilename.equals("")) {
@@ -121,7 +134,7 @@ public class DecryptFileFragment extends DecryptFragment {
return;
}
- if (mInputFilename.startsWith("file")) {
+ if (mInputUri == null && mInputFilename.startsWith("file")) {
File file = new File(mInputFilename);
if (!file.exists() || !file.isFile()) {
AppMsg.makeText(
@@ -143,7 +156,11 @@ public class DecryptFileFragment extends DecryptFragment {
public void handleMessage(Message message) {
if (message.what == FileDialogFragment.MESSAGE_OKAY) {
Bundle data = message.getData();
- mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+ if (data.containsKey(FileDialogFragment.MESSAGE_DATA_URI)) {
+ mOutputUri = data.getParcelable(FileDialogFragment.MESSAGE_DATA_URI);
+ } else {
+ mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+ }
decryptStart(null);
}
}
@@ -172,13 +189,25 @@ public class DecryptFileFragment extends DecryptFragment {
intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
// data
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
-
Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
- + mOutputFilename);
+ + mOutputFilename + ",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.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
- data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
+ 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.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase);
@@ -209,8 +238,13 @@ public class DecryptFileFragment extends DecryptFragment {
if (mDeleteAfter.isChecked()) {
// Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
- .newInstance(mInputFilename);
+ DeleteFileDialogFragment deleteFileDialog;
+ if (mInputUri != null) {
+ deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);
+ } else {
+ deleteFileDialog = DeleteFileDialogFragment
+ .newInstance(mInputFilename);
+ }
deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
}
}
@@ -234,13 +268,25 @@ public class DecryptFileFragment extends DecryptFragment {
switch (requestCode) {
case RESULT_CODE_FILE: {
if (resultCode == Activity.RESULT_OK && data != null) {
- try {
- String path = FileHelper.getPath(getActivity(), data.getData());
- Log.d(Constants.TAG, "path=" + path);
+ 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!");
+ mFilename.setText(path);
+ } catch (NullPointerException e) {
+ Log.e(Constants.TAG, "Nullpointer while retrieving path!");
+ }
}
}
return;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
index d1ad7fbc5..46462f924 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
@@ -129,7 +129,7 @@ public class DecryptMessageFragment extends DecryptFragment {
intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
// data
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES);
data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes());
data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase);
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 d150abdeb..2671e0d40 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
@@ -20,11 +20,13 @@ 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.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;
@@ -73,7 +75,9 @@ public class EncryptFileFragment extends Fragment {
// model
private String mInputFilename = null;
+ private Uri mInputUri = null;
private String mOutputFilename = null;
+ private Uri mOutputUri = null;
@Override
public void onAttach(Activity activity) {
@@ -104,8 +108,12 @@ public class EncryptFileFragment extends Fragment {
mBrowse = (BootstrapButton) view.findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
- FileHelper.openFile(EncryptFileFragment.this, mFilename.getText().toString(), "*/*",
- RESULT_CODE_FILE);
+ if (Constants.KITKAT) {
+ FileHelper.openDocument(EncryptFileFragment.this, mInputUri, "*/*", RESULT_CODE_FILE);
+ } else {
+ FileHelper.openFile(EncryptFileFragment.this, mFilename.getText().toString(), "*/*",
+ RESULT_CODE_FILE);
+ }
}
});
@@ -178,7 +186,11 @@ public class EncryptFileFragment extends Fragment {
public void handleMessage(Message message) {
if (message.what == FileDialogFragment.MESSAGE_OKAY) {
Bundle data = message.getData();
- mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+ if (data.containsKey(FileDialogFragment.MESSAGE_DATA_URI)) {
+ mOutputUri = data.getParcelable(FileDialogFragment.MESSAGE_DATA_URI);
+ } else {
+ mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+ }
encryptStart();
}
}
@@ -197,17 +209,20 @@ public class EncryptFileFragment extends Fragment {
private void encryptClicked() {
String currentFilename = mFilename.getText().toString();
if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
+ mInputUri = null;
mInputFilename = mFilename.getText().toString();
}
- mOutputFilename = guessOutputFilename(mInputFilename);
+ if (mInputUri == null) {
+ mOutputFilename = guessOutputFilename(mInputFilename);
+ }
if (mInputFilename.equals("")) {
AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
return;
}
- if (!mInputFilename.startsWith("content")) {
+ if (mInputUri == null && !mInputFilename.startsWith("content")) {
File file = new File(mInputFilename);
if (!file.exists() || !file.isFile()) {
AppMsg.makeText(
@@ -280,7 +295,25 @@ public class EncryptFileFragment extends Fragment {
// fill values for this action
Bundle data = new Bundle();
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
+ 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);
+ } else {
+ data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_FILE);
+ data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
+ }
+
+ 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);
+ }
if (mEncryptInterface.isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!");
@@ -296,12 +329,6 @@ public class EncryptFileFragment extends Fragment {
mEncryptInterface.getEncryptionKeys());
}
- Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
- + mOutputFilename);
-
- data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
- data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
-
boolean useAsciiArmor = mAsciiArmor.isChecked();
data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, useAsciiArmor);
@@ -323,8 +350,13 @@ public class EncryptFileFragment extends Fragment {
if (mDeleteAfter.isChecked()) {
// Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
- .newInstance(mInputFilename);
+ DeleteFileDialogFragment deleteFileDialog;
+ if (mInputUri != null) {
+ deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);
+ } else {
+ deleteFileDialog = DeleteFileDialogFragment
+ .newInstance(mInputFilename);
+ }
deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
}
@@ -332,7 +364,11 @@ public class EncryptFileFragment extends Fragment {
// Share encrypted file
Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
sendFileIntent.setType("*/*");
- sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
+ 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)));
}
@@ -356,13 +392,25 @@ public class EncryptFileFragment extends Fragment {
switch (requestCode) {
case RESULT_CODE_FILE: {
if (resultCode == Activity.RESULT_OK && data != null) {
- 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 (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!");
+ }
}
}
return;
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 4c35806e5..8a6103b16 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
@@ -177,7 +177,7 @@ public class EncryptMessageFragment extends Fragment {
// fill values for this action
Bundle data = new Bundle();
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES);
String message = mMessage.getText().toString();
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 b42a79993..cae6cf043 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
@@ -21,9 +21,11 @@ import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
+import android.net.Uri;
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;
@@ -34,6 +36,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
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
@@ -50,12 +53,27 @@ public class DeleteFileDialogFragment extends DialogFragment {
}
/**
+ * Creates new instance of this delete file dialog fragment
+ */
+ public static DeleteFileDialogFragment newInstance(Uri deleteUri) {
+ DeleteFileDialogFragment frag = new DeleteFileDialogFragment();
+ Bundle args = new Bundle();
+
+ args.putParcelable(ARG_DELETE_URI, deleteUri);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
* Creates dialog
*/
@Override
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);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
@@ -71,6 +89,12 @@ 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);
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 24f93bed7..10a24ddf0 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
@@ -23,10 +23,13 @@ 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;
@@ -50,6 +53,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_CHECKED = "checked";
@@ -60,6 +64,9 @@ public class FileDialogFragment extends DialogFragment {
private CheckBox mCheckBox;
private TextView mMessageTextView;
+ private String mOutputFilename;
+ private Uri mOutputUri;
+
private static final int REQUEST_CODE = 0x00007004;
/**
@@ -92,7 +99,7 @@ public class FileDialogFragment extends DialogFragment {
String title = getArguments().getString(ARG_TITLE);
String message = getArguments().getString(ARG_MESSAGE);
- String defaultFile = getArguments().getString(ARG_DEFAULT_FILE);
+ mOutputFilename = getArguments().getString(ARG_DEFAULT_FILE);
String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT);
LayoutInflater inflater = (LayoutInflater) activity
@@ -106,15 +113,18 @@ public class FileDialogFragment extends DialogFragment {
mMessageTextView.setText(message);
mFilename = (EditText) view.findViewById(R.id.input);
- mFilename.setText(defaultFile);
+ mFilename.setText(mOutputFilename);
mBrowse = (BootstrapButton) 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!
- FileHelper.openFile(FileDialogFragment.this, mFilename.getText().toString(), "*/*",
- REQUEST_CODE);
+ if (Constants.KITKAT) {
+ FileHelper.saveDocument(FileDialogFragment.this, mOutputUri, "*/*", REQUEST_CODE);
+ } else {
+ FileHelper.openFile(FileDialogFragment.this, mOutputFilename, "*/*", REQUEST_CODE);
+ }
}
});
@@ -136,13 +146,19 @@ public class FileDialogFragment extends DialogFragment {
public void onClick(DialogInterface dialog, int id) {
dismiss();
- boolean checked = false;
- if (mCheckBox.isEnabled()) {
- checked = mCheckBox.isChecked();
+ String currentFilename = mFilename.getText().toString();
+ if (mOutputFilename == null || !mOutputFilename.equals(currentFilename)) {
+ mOutputUri = null;
+ mOutputFilename = mFilename.getText().toString();
}
+ 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.putBoolean(MESSAGE_DATA_CHECKED, checked);
@@ -178,14 +194,26 @@ public class FileDialogFragment extends DialogFragment {
switch (requestCode & 0xFFFF) {
case REQUEST_CODE: {
if (resultCode == Activity.RESULT_OK && data != null) {
- 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);
+ 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();
+ }
+ } 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);
+ }
}
}