From 79fb23b095fba273d77066204ee44d2b8d1edf8d Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sun, 22 Jun 2014 16:31:28 +0200 Subject: Improve file more, Part 1 - Use Uris where it makes sense, Use File class to clarify it's a file (and not whatever else a string could be) - Show sdcard in side menu in storage API #665 - Propose filename with gpg ending when storing it using the storage API #665 - Don't show output dialog on Android 4.4 #665 - Only show filename on Android < 4.4 #665 TODO: - File deletion for Android < 4.4 - Testing (especially with Android < 4.4) - Batch-encryption - UI - Temporary content provider (see #665 discussion) --- .../org/sufficientlysecure/keychain/Constants.java | 7 +- .../keychain/KeychainApplication.java | 5 +- .../keychain/helper/ExportHelper.java | 63 +++---- .../keychain/helper/FileHelper.java | 149 +++++++++-------- .../keychain/service/KeychainIntentService.java | 21 ++- .../keychain/ui/DecryptActivity.java | 21 +-- .../keychain/ui/DecryptFileFragment.java | 171 ++++++------------- .../keychain/ui/EncryptActivity.java | 29 +--- .../keychain/ui/EncryptFileFragment.java | 181 ++++++--------------- .../keychain/ui/ImportKeysFileFragment.java | 2 +- .../keychain/ui/KeyListFragment.java | 8 +- .../keychain/ui/ViewKeyActivity.java | 4 +- .../ui/dialog/DeleteFileDialogFragment.java | 38 ++--- .../keychain/ui/dialog/FileDialogFragment.java | 87 ++++------ OpenKeychain/src/main/res/values/strings.xml | 1 + 15 files changed, 291 insertions(+), 496 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 319ac2873..8074ad2ee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -27,6 +27,8 @@ 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; @@ -52,9 +54,8 @@ public final class Constants { 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"; - 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 { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index 5d6a62f9c..d28c4d63c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -26,11 +26,9 @@ import android.graphics.drawable.Drawable; import android.os.Environment; import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.sufficientlysecure.keychain.helper.ContactHelper; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.PRNGFixes; -import java.io.File; import java.security.Provider; import java.security.Security; @@ -70,8 +68,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 } 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..ae9438148 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java @@ -30,7 +30,6 @@ 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; @@ -39,9 +38,10 @@ 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 +68,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 +108,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..2898c7030 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java @@ -26,12 +26,19 @@ import android.database.Cursor; 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.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; public class FileHelper { @@ -55,25 +62,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,19 +84,62 @@ 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); + } + /** * 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) { + public static void openDocument(Fragment fragment, 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); } @@ -104,66 +147,42 @@ public class FileHelper { /** * 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 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); - - intent.setData(Uri.parse("file://" + filename)); - intent.setType(mimeType); - - return intent; - } + 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); - /** - * Get a file path from a Uri. - *

- * 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(); + if (cursor != null) { + if (cursor.moveToNext()) { + filename = cursor.getString(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 } + if (filename == null) { + String[] split = uri.toString().split("/"); + filename = split[split.length - 1]; + } + return filename; + } - return null; + public static interface FileDialogCallback { + public void onFileSelected(File file, boolean checked); } } 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 e1514b16f..a2676b1a4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -139,6 +139,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"; @@ -393,13 +394,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 publicMasterKeyIds = new ArrayList(); @@ -431,12 +435,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) { boolean isDeleted = new File(outputFile).delete(); } 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 5b21be6e4..33659f3e5 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)) { @@ -155,21 +153,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 { 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 28a465436..3df3b24b2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -20,21 +20,17 @@ 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; import android.widget.CheckBox; -import android.widget.EditText; +import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; -import com.devspark.appmsg.AppMsg; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -44,29 +40,26 @@ 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 java.io.File; public class DecryptFileFragment extends DecryptFragment { - public static final String ARG_FILENAME = "filename"; + public static final String ARG_URI = "uri"; - 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 BootstrapButton 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 */ @@ -74,17 +67,17 @@ 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); + mFilename = (TextView) view.findViewById(R.id.decrypt_file_filename); mBrowse = (BootstrapButton) view.findViewById(R.id.decrypt_file_browse); 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() { public void onClick(View v) { if (Constants.KITKAT) { - FileHelper.openDocument(DecryptFileFragment.this, mInputUri, "*/*", RESULT_CODE_FILE); + 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); } } }); @@ -102,78 +95,48 @@ 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().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("")) { //AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show(); 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()) { - AppMsg.makeText( - getActivity(), - getString(R.string.error_message, - getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT) - .show(); - 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 (!Constants.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 @@ -189,25 +152,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); @@ -238,14 +189,9 @@ 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); } } } @@ -266,28 +212,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/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 39d4a09bc..e93a63cc8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.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.ui.adapter.PagerTabStripAdapter; import org.sufficientlysecure.keychain.util.Log; @@ -98,11 +96,7 @@ public class EncryptActivity extends DrawerActivity implements @Override public boolean isModeSymmetric() { - if (PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem()) { - return true; - } else { - return false; - } + return PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem(); } @Override @@ -201,7 +195,7 @@ public class EncryptActivity extends DrawerActivity implements } } else { // Files via content provider, override uri and action - uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); + uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); action = ACTION_ENCRYPT; } } @@ -232,23 +226,8 @@ public class EncryptActivity extends DrawerActivity implements mSwitchToContent = PAGER_CONTENT_MESSAGE; } else if (ACTION_ENCRYPT.equals(action) && uri != null) { // 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.putParcelable(EncryptFileFragment.ARG_URI, uri); + mSwitchToContent = PAGER_CONTENT_FILE; } else { 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/EncryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java index 2671e0d40..4da76bdfb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java @@ -20,21 +20,19 @@ 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; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.CheckBox; -import android.widget.EditText; import android.widget.Spinner; +import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; import com.devspark.appmsg.AppMsg; @@ -47,7 +45,6 @@ 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; @@ -55,28 +52,25 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.File; public class EncryptFileFragment extends Fragment { - public static final String ARG_FILENAME = "filename"; + public static final String ARG_URI = "uri"; public static final String ARG_ASCII_ARMOR = "ascii_armor"; - private static final int RESULT_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 TextView mFilename = null; private CheckBox mDeleteAfter = null; private CheckBox mShareAfter = null; private BootstrapButton mBrowse = null; private View mEncryptFile; - private FileDialogFragment mFileDialog; - // model - private String mInputFilename = null; private Uri mInputUri = null; - private String mOutputFilename = null; private Uri mOutputUri = null; @Override @@ -104,15 +98,15 @@ public class EncryptFileFragment extends Fragment { } }); - mFilename = (EditText) view.findViewById(R.id.filename); + mFilename = (TextView) view.findViewById(R.id.filename); mBrowse = (BootstrapButton) view.findViewById(R.id.btn_browse); mBrowse.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (Constants.KITKAT) { - FileHelper.openDocument(EncryptFileFragment.this, mInputUri, "*/*", RESULT_CODE_FILE); + FileHelper.openDocument(EncryptFileFragment.this, "*/*", REQUEST_CODE_INPUT); } else { - FileHelper.openFile(EncryptFileFragment.this, mFilename.getText().toString(), "*/*", - RESULT_CODE_FILE); + FileHelper.openFile(EncryptFileFragment.this, mInputUri, "*/*", + REQUEST_CODE_INPUT); } } }); @@ -154,86 +148,45 @@ 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); - } + setInputUri(getArguments().getParcelable(ARG_URI)); boolean asciiArmor = getArguments().getBoolean(ARG_ASCII_ARMOR); if (asciiArmor) { - mAsciiArmor.setChecked(asciiArmor); + mAsciiArmor.setChecked(true); } } - /** - * 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; + private void setInputUri(Uri inputUri) { + if (inputUri == null) { + mInputUri = null; + mFilename.setText(""); + return; + } - return outputFilename; + mInputUri = inputUri; + mFilename.setText(FileHelper.getFilename(getActivity(), mInputUri)); } 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(); - } - } - }; - - // 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"); + if (!Constants.KITKAT) { + File file = new File(mInputUri.getPath()); + File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; + String targetName = FileHelper.getFilename( + getActivity(), mInputUri) + (mAsciiArmor.isChecked() ? ".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 { + FileHelper.saveDocument(this, "*/*", FileHelper.getFilename(getActivity(), mInputUri) + + (mAsciiArmor.isChecked() ? ".asc" : ".gpg"), REQUEST_CODE_OUTPUT); + } } 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("")) { AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show(); return; } - if (mInputUri == null && !mInputFilename.startsWith("content")) { - File file = new File(mInputFilename); - if (!file.exists() || !file.isFile()) { - AppMsg.makeText( - getActivity(), - getString(R.string.error_message, - getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT) - .show(); - return; - } - } - if (mEncryptInterface.isModeSymmetric()) { // symmetric encryption @@ -287,6 +240,10 @@ public class EncryptFileFragment extends Fragment { } private void encryptStart() { + if (mInputUri == null || mOutputUri == null) { + throw new IllegalStateException("Something went terribly wrong if this happens!"); + } + // Send all information needed to service to edit key in other thread Intent intent = new Intent(getActivity(), KeychainIntentService.class); @@ -295,25 +252,13 @@ public class EncryptFileFragment extends Fragment { // fill values for this action Bundle data = new Bundle(); - 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); if (mEncryptInterface.isModeSymmetric()) { Log.d(Constants.TAG, "Symmetric encryption enabled!"); @@ -350,25 +295,16 @@ public class EncryptFileFragment extends Fragment { 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); } 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)); - } + sendFileIntent.putExtra(Intent.EXTRA_STREAM, mOutputUri); startActivity(Intent.createChooser(sendFileIntent, getString(R.string.title_share_file))); } @@ -390,28 +326,17 @@ public class EncryptFileFragment extends Fragment { @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(); + encryptStart(); } return; } 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 60e5324c5..f24cb379e 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/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 5eb8ecb8d..48ad13425 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -181,8 +181,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: { @@ -205,7 +205,7 @@ public class KeyListFragment extends LoaderFragment public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { if (checked) { - mAdapter.setNewSelection(position, checked); + mAdapter.setNewSelection(position, true); } else { mAdapter.removeSelection(position); } @@ -452,7 +452,7 @@ public class KeyListFragment extends LoaderFragment ItemViewHolder holder = new ItemViewHolder(); holder.mMainUserId = (TextView) view.findViewById(R.id.mainUserId); holder.mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - holder.mStatusDivider = (View) view.findViewById(R.id.status_divider); + holder.mStatusDivider = view.findViewById(R.id.status_divider); holder.mStatusLayout = (FrameLayout) view.findViewById(R.id.status_layout); holder.mButton = (ImageButton) view.findViewById(R.id.edit); holder.mRevoked = (TextView) view.findViewById(R.id.revoked); 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 1912b6e7d..6dc6990e0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.ui; import android.annotation.TargetApi; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; @@ -312,8 +311,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/dialog/DeleteFileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java index cae6cf043..f9111d885 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,40 +18,20 @@ 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.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.Constants; 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 */ @@ -74,14 +54,14 @@ public class DeleteFileDialogFragment extends DialogFragment { final FragmentActivity activity = getActivity(); final Uri deleteUri = getArguments().containsKey(ARG_DELETE_URI) ? getArguments().getParcelable(ARG_DELETE_URI) : null; - final String deleteFile = getArguments().getString(ARG_DELETE_FILE); + 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,12 +69,14 @@ public class DeleteFileDialogFragment extends DialogFragment { public void onClick(DialogInterface dialog, int id) { dismiss(); - if (deleteUri != null) { + if (Constants.KITKAT) { // We can not securely delete Documents, so just use usual delete on them - DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri); - return; + if (DocumentsContract.deleteDocument(getActivity().getContentResolver(), deleteUri)) return; } + // TODO!!! We can't delete files from Uri without trying to find it's real path + + /* // Send all information needed to service to edit key in other thread Intent intent = new Intent(activity, KeychainIntentService.class); @@ -102,7 +84,6 @@ public class DeleteFileDialogFragment extends DialogFragment { 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( @@ -134,6 +115,7 @@ public class DeleteFileDialogFragment extends DialogFragment { // start service with intent activity.startService(intent); + */ } }); alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 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 10a24ddf0..c02c37055 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,13 +23,11 @@ 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; @@ -39,11 +37,17 @@ import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; +import com.devspark.appmsg.AppMsg; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.util.Log; +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"; @@ -53,8 +57,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; @@ -64,8 +67,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; @@ -73,14 +75,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); @@ -99,7 +101,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 @@ -113,18 +119,14 @@ public class FileDialogFragment extends DialogFragment { mMessageTextView.setText(message); mFilename = (EditText) view.findViewById(R.id.input); - mFilename.setText(mOutputFilename); + mFilename.setText(mFile.getName()); 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! - 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); } }); @@ -147,19 +149,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); @@ -176,44 +182,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); - } + AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show(); } } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index e24ac6925..5600aa06e 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -195,6 +195,7 @@ Wrong passphrase. Set a passphrase first. No compatible file manager installed. + The file manager does not support saving. The passphrases didn\'t match. Please enter a passphrase. Symmetric encryption. -- cgit v1.2.3