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) --- .../keychain/helper/ExportHelper.java | 63 ++++----- .../keychain/helper/FileHelper.java | 149 ++++++++++++--------- 2 files changed, 107 insertions(+), 105 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper') 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); } } -- cgit v1.2.3