/* * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.sufficientlysecure.keychain.util; 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.graphics.Bitmap; import android.graphics.Point; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.provider.DocumentsContract; import android.provider.OpenableColumns; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.widget.Toast; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; import java.io.File; import java.text.DecimalFormat; public class FileHelper { /** * Checks if external storage is mounted if file is located on external storage * * @param file * @return true if storage is mounted */ public static boolean isStorageMounted(String file) { if (file.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { return false; } } return true; } /** * Opens the preferred installed file manager on Android and shows a toast if no manager is * installed. * * @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(Fragment fragment, Uri last, String mimeType, int requestCode) { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setData(last); intent.setType(mimeType); try { fragment.startActivityForResult(intent, requestCode); } catch (ActivityNotFoundException e) { // No compatible file manager was found. Toast.makeText(fragment.getActivity(), R.string.no_filemanager_installed, Toast.LENGTH_SHORT).show(); } } public static void saveFile(final FileDialogCallback callback, final FragmentManager fragmentManager, final String title, final String message, final File defaultFile, final String checkMsg) { // Message is received after file is selected Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { if (message.what == FileDialogFragment.MESSAGE_OKAY) { callback.onFileSelected( new File(message.getData().getString(FileDialogFragment.MESSAGE_DATA_FILE)), message.getData().getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED)); } } }; // Create a new Messenger for the communication back final Messenger messenger = new Messenger(returnHandler); DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { @Override public void run() { FileDialogFragment fileDialog = FileDialogFragment.newInstance(messenger, title, message, defaultFile, checkMsg); fileDialog.show(fragmentManager, "fileDialog"); } }); } public static void saveFile(Fragment fragment, String title, String message, File defaultFile, int requestCode) { saveFile(fragment, title, message, defaultFile, requestCode, null); } public static void saveFile(final Fragment fragment, String title, String message, File defaultFile, final int requestCode, String checkMsg) { saveFile(new FileDialogCallback() { @Override public void onFileSelected(File file, boolean checked) { Intent intent = new Intent(); intent.setData(Uri.fromFile(file)); fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent); } }, fragment.getActivity().getSupportFragmentManager(), title, message, defaultFile, checkMsg); } @TargetApi(Build.VERSION_CODES.KITKAT) public static void openDocument(Fragment fragment, String mimeType, int requestCode) { openDocument(fragment, mimeType, false, requestCode); } /** * Opens the storage browser on Android 4.4 or later for opening a file * * @param fragment * @param mimeType can be text/plain for example * @param multiple allow file chooser to return multiple files * @param requestCode used to identify the result coming back from storage browser onActivityResult() in your */ @TargetApi(Build.VERSION_CODES.KITKAT) public static void openDocument(Fragment fragment, String mimeType, boolean multiple, int requestCode) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType(mimeType); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple); fragment.startActivityForResult(intent, requestCode); } /** * Opens the storage browser on Android 4.4 or later for saving a file * * @param fragment * @param 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, String mimeType, String suggestedName, int requestCode) { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); 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); } 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); if (cursor != null) { if (cursor.moveToNext()) { filename = cursor.getString(0); } cursor.close(); } } catch (Exception ignored) { // This happens in rare cases (eg: document deleted since selection) and should not cause a failure } if (filename == null) { String[] split = uri.toString().split("/"); filename = split[split.length - 1]; } return filename; } public static long getFileSize(Context context, Uri uri) { return getFileSize(context, uri, -1); } public static long getFileSize(Context context, Uri uri, long def) { long size = def; try { Cursor cursor = context.getContentResolver().query(uri, new String[]{OpenableColumns.SIZE}, null, null, null); if (cursor != null) { if (cursor.moveToNext()) { size = cursor.getLong(0); } cursor.close(); } } catch (Exception ignored) { // This happens in rare cases (eg: document deleted since selection) and should not cause a failure } return size; } /** * Retrieve thumbnail of file, document api feature and thus KitKat only */ public static Bitmap getThumbnail(Context context, Uri uri, Point size) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return DocumentsContract.getDocumentThumbnail(context.getContentResolver(), uri, size, null); } else { return null; } } public static String readableFileSize(long size) { if (size <= 0) return "0"; final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"}; int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; } public static interface FileDialogCallback { public void onFileSelected(File file, boolean checked); } }