From 3dbb677afe4e865e85b4e1da1df360e1fc5d28cc Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Sun, 6 Jan 2013 22:50:00 -0800 Subject: [PATCH 1/6] Build on Android 10, use newer FileUtils --- aFileChooser/project.properties | 2 +- .../afilechooser/utils/FileUtils.java | 192 ++++++++++++++++-- 2 files changed, 179 insertions(+), 15 deletions(-) diff --git a/aFileChooser/project.properties b/aFileChooser/project.properties index acfc74e..f9e4d0b 100644 --- a/aFileChooser/project.properties +++ b/aFileChooser/project.properties @@ -8,5 +8,5 @@ # project structure. # Project target. -target=android-16 +target=android-10 android.library=true diff --git a/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java b/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java index e753a74..ab49760 100644 --- a/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java +++ b/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java @@ -17,11 +17,17 @@ package com.ipaulpro.afilechooser.utils; import java.io.File; +import java.io.FileFilter; import java.net.URISyntaxException; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.res.XmlResourceParser; import android.database.Cursor; import android.graphics.Bitmap; @@ -42,14 +48,13 @@ public class FileUtils { /** TAG for log messages. */ static final String TAG = "FileUtils"; - private static final boolean DEBUG = true; // Set to false to disable logging + private static final boolean DEBUG = false; // Set to true to enable logging - public static final String TYPE_AUDIO = "audio"; - public static final String TYPE_DOC = "doc"; - public static final String TYPE_IMAGE = "image"; - public static final String TYPE_OTHER = "other"; - public static final String TYPE_VIDEO = "video"; - public static final String TYPE_APP = "app"; + public static final String MIME_TYPE_AUDIO = "audio/*"; + public static final String MIME_TYPE_TEXT = "text/*"; + public static final String MIME_TYPE_IMAGE = "image/*"; + public static final String MIME_TYPE_VIDEO = "video/*"; + public static final String MIME_TYPE_APP = "application/*"; /** * Whether the filename is a video file. @@ -106,11 +111,12 @@ public static String getExtension(String uri) { * @param uri * @return */ - public static boolean isMediaUri(String uri) { - if (uri.startsWith(Audio.Media.INTERNAL_CONTENT_URI.toString()) - || uri.startsWith(Audio.Media.EXTERNAL_CONTENT_URI.toString()) - || uri.startsWith(Video.Media.INTERNAL_CONTENT_URI.toString()) - || uri.startsWith(Video.Media.EXTERNAL_CONTENT_URI.toString())) { + public static boolean isMediaUri(Uri uri) { + String uriString = uri.toString(); + if (uriString.startsWith(Audio.Media.INTERNAL_CONTENT_URI.toString()) + || uriString.startsWith(Audio.Media.EXTERNAL_CONTENT_URI.toString()) + || uriString.startsWith(Video.Media.INTERNAL_CONTENT_URI.toString()) + || uriString.startsWith(Video.Media.EXTERNAL_CONTENT_URI.toString())) { return true; } else { return false; @@ -191,6 +197,16 @@ public static File getFile(File curdir, String file) { } + /** + * Get a file path from a Uri. + * + * @param context + * @param uri + * @return + * @throws URISyntaxException + * + * @author paulburke + */ public static String getPath(Context context, Uri uri) throws URISyntaxException { if(DEBUG) Log.d(TAG+" File -", @@ -226,6 +242,14 @@ else if ("file".equalsIgnoreCase(uri.getScheme())) { return null; } + /** + * Get the file size in a human-readable string. + * + * @param size + * @return + * + * @author paulburke + */ public static String getReadableFileSize(int size) { final int BYTES_IN_KILOBYTES = 1024; final DecimalFormat dec = new DecimalFormat("###.#"); @@ -251,10 +275,12 @@ public static String getReadableFileSize(int size) { } /** + * Load MIME types from XML + * * @param context * @return */ - public static MimeTypes getMimeTypes(Context context) { + private static MimeTypes getMimeTypes(Context context) { MimeTypes mimeTypes = null; final MimeTypeParser mtp = new MimeTypeParser(); final XmlResourceParser in = context.getResources().getXml(R.xml.mimetypes); @@ -268,6 +294,8 @@ public static MimeTypes getMimeTypes(Context context) { } /** + * Get the file MIME type + * * @param context * @param file * @return @@ -280,13 +308,55 @@ public static String getMimeType(Context context, File file) { } /** + * Attempt to retrieve the thumbnail of given File from the MediaStore. + * + * This should not be called on the UI thread. + * + * @param context + * @param file + * @return + * + * @author paulburke + */ + public static Bitmap getThumbnail(Context context, File file) { + return getThumbnail(context, getUri(file), getMimeType(context, file)); + } + + /** + * Attempt to retrieve the thumbnail of given Uri from the MediaStore. + * + * This should not be called on the UI thread. + * + * @param context + * @param uri + * @return + * + * @author paulburke + */ + public static Bitmap getThumbnail(Context context, Uri uri) { + return getThumbnail(context, uri, getMimeType(context, getFile(uri))); + } + + /** + * Attempt to retrieve the thumbnail of given Uri from the MediaStore. + * + * This should not be called on the UI thread. + * * @param context * @param uri * @param mimeType * @return + * + * @author paulburke */ public static Bitmap getThumbnail(Context context, Uri uri, String mimeType) { if(DEBUG) Log.d(TAG, "Attempting to get thumbnail"); + + if (isMediaUri(uri)) { + Log.e(TAG, "You can only retrieve thumbnails for images and videos."); + return null; + } + Bitmap bm = null; if (uri != null) { final ContentResolver resolver = context.getContentResolver(); @@ -304,7 +374,7 @@ public static Bitmap getThumbnail(Context context, Uri uri, String mimeType) { MediaStore.Video.Thumbnails.MINI_KIND, null); } - else if (mimeType.contains("image")) { + else if (mimeType.contains(FileUtils.MIME_TYPE_IMAGE)) { bm = MediaStore.Images.Thumbnails.getThumbnail( resolver, id, @@ -320,4 +390,98 @@ else if (mimeType.contains("image")) { } return bm; } + + private static final String HIDDEN_PREFIX = "."; + + /** + * File and folder comparator. + * TODO Expose sorting option method + * + * @author paulburke + */ + private static Comparator mComparator = new Comparator() { + public int compare(File f1, File f2) { + // Sort alphabetically by lower case, which is much cleaner + return f1.getName().toLowerCase().compareTo( + f2.getName().toLowerCase()); + } + }; + + /** + * File (not directories) filter. + * + * @author paulburke + */ + private static FileFilter mFileFilter = new FileFilter() { + public boolean accept(File file) { + final String fileName = file.getName(); + // Return files only (not directories) and skip hidden files + return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX); + } + }; + + /** + * Folder (directories) filter. + * + * @author paulburke + */ + private static FileFilter mDirFilter = new FileFilter() { + public boolean accept(File file) { + final String fileName = file.getName(); + // Return directories only and skip hidden directories + return file.isDirectory() && !fileName.startsWith(HIDDEN_PREFIX); + } + }; + + /** + * Get a list of Files in the give path + * + * @param path + * @return Collection of files in give directory + + * @author paulburke + */ + public static List getFileList(String path) { + ArrayList list = new ArrayList(); + + // Current directory File instance + final File pathDir = new File(path); + + // List file in this directory with the directory filter + final File[] dirs = pathDir.listFiles(mDirFilter); + if (dirs != null) { + // Sort the folders alphabetically + Arrays.sort(dirs, mComparator); + // Add each folder to the File list for the list adapter + for (File dir : dirs) list.add(dir); + } + + // List file in this directory with the file filter + final File[] files = pathDir.listFiles(mFileFilter); + if (files != null) { + // Sort the files alphabetically + Arrays.sort(files, mComparator); + // Add each file to the File list for the list adapter + for (File file : files) list.add(file); + } + + return list; + } + + /** + * Get the Intent for selecting content to be used in an Intent Chooser. + * + * @return The intent for opening a file with Intent.createChooser() + * + * @author paulburke + */ + public static Intent createGetContentIntent() { + // Implicitly allow the user to select a particular kind of data + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + // The MIME data type filter + intent.setType("*/*"); + // Only return URIs that can be opened with ContentResolver + intent.addCategory(Intent.CATEGORY_OPENABLE); + return intent; + } } From 29ee2b4bcbfd05434ea283cd4b8cf28a5b7b8f1a Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Wed, 9 Jan 2013 12:35:02 -0800 Subject: [PATCH 2/6] Obey MIME types from intent --- .../src/com/ipaulpro/afilechooser/FileChooserActivity.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java b/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java index 07d1099..fc0b138 100644 --- a/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java +++ b/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Paul Burke + * (Modified by Zhuowei Zhang for the PocketInvEditor project) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -70,7 +71,8 @@ public class FileChooserActivity extends ListActivity { public boolean accept(File file) { final String fileName = file.getName(); // Return files only (not directories) and skip hidden files - return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX); + return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX) && + FileUtils.getMimeType(FileChooserActivity.this, file).equals(getIntent().getType()); } }; @@ -423,4 +425,4 @@ private void restoreMe(Bundle state) { this.mBreadcrumb = state.getStringArrayList(BREADCRUMB); fillList(state.getInt(POSTIION)); } -} \ No newline at end of file +} From 4e643c9fed22f5528cb9a3dfd5df034ae9351b47 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 19 Feb 2013 21:04:19 -0800 Subject: [PATCH 3/6] Added build.xml for building with Ant --- aFileChooser/build.xml | 92 +++++++++++++++++++++++++++++++ aFileChooser/proguard-project.txt | 20 +++++++ 2 files changed, 112 insertions(+) create mode 100644 aFileChooser/build.xml create mode 100644 aFileChooser/proguard-project.txt diff --git a/aFileChooser/build.xml b/aFileChooser/build.xml new file mode 100644 index 0000000..98d72f9 --- /dev/null +++ b/aFileChooser/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aFileChooser/proguard-project.txt b/aFileChooser/proguard-project.txt new file mode 100644 index 0000000..f2fe155 --- /dev/null +++ b/aFileChooser/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} From 32dca2d98f0e2fc39ad9e997257f17201593b759 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Thu, 30 Jan 2014 11:46:20 -0500 Subject: [PATCH 4/6] Support picking of multiple mimetypes --- .../ipaulpro/afilechooser/FileChooserActivity.java | 14 +++++++++++++- .../com/ipaulpro/afilechooser/utils/FileUtils.java | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java b/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java index fc0b138..e751602 100644 --- a/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java +++ b/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java @@ -22,6 +22,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; import android.app.ListActivity; import android.content.BroadcastReceiver; @@ -63,6 +65,7 @@ public class FileChooserActivity extends ListActivity { private File mExternalDir; private ArrayList mList = new ArrayList(); + private Set extendedMimeTypes = new HashSet(); /** * File (not directories) filter. @@ -70,9 +73,10 @@ public class FileChooserActivity extends ListActivity { private FileFilter mFileFilter = new FileFilter() { public boolean accept(File file) { final String fileName = file.getName(); + final String mimeType = FileUtils.getMimeType(FileChooserActivity.this, file); // Return files only (not directories) and skip hidden files return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX) && - FileUtils.getMimeType(FileChooserActivity.this, file).equals(getIntent().getType()); + (mimeType.equals(getIntent().getType()) || extendedMimeTypes.contains(mimeType)); } }; @@ -326,6 +330,8 @@ protected void onCreate(Bundle savedInstanceState) { } else { // Set the external storage directory as the current path this.mPath = this.mExternalDir.getAbsolutePath(); + String startPath = getIntent().getStringExtra("startPath"); + if (startPath != null) this.mPath = startPath; // Add the current path to the breadcrumb updateBreadcrumb(true); @@ -334,6 +340,12 @@ protected void onCreate(Bundle savedInstanceState) { fillList(0); } } + String[] extraMimeTypes = getIntent().getStringArrayExtra(FileUtils.EXTRA_MIME_TYPES); + extendedMimeTypes.clear(); + if (extraMimeTypes != null) { + for (String s: extraMimeTypes) + extendedMimeTypes.add(s); + } } @Override diff --git a/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java b/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java index ab49760..dfc8171 100644 --- a/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java +++ b/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java @@ -56,6 +56,8 @@ public class FileUtils { public static final String MIME_TYPE_VIDEO = "video/*"; public static final String MIME_TYPE_APP = "application/*"; + public static final String EXTRA_MIME_TYPES = "net.zhuoweizhang.afilechooser.extra.MIME_TYPES"; + /** * Whether the filename is a video file. * From 5db60c9d2eb0d18e1a84e0f8cbfdc13400252660 Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Tue, 15 Apr 2014 22:09:11 -0400 Subject: [PATCH 5/6] Added sort order --- .../afilechooser/FileChooserActivity.java | 16 ++++++++++++++++ .../ipaulpro/afilechooser/utils/FileUtils.java | 2 ++ 2 files changed, 18 insertions(+) diff --git a/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java b/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java index e751602..7f89eb4 100644 --- a/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java +++ b/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java @@ -103,6 +103,15 @@ public int compare(File f1, File f2) { } }; + private Comparator mLastModifiedComparator = new Comparator() { + public int compare(File f1, File f2) { + long a = f1.lastModified(); + long b = f2.lastModified(); + if (a == b) return 0; + return a < b? 1: -1; + } + }; + /** * External storage state broadcast receiver. */ @@ -320,6 +329,13 @@ protected void onCreate(Bundle savedInstanceState) { // Get the external storage directory. this.mExternalDir = Environment.getExternalStorageDirectory(); + String sortMethod = getIntent().getStringExtra(FileUtils.EXTRA_SORT_METHOD); + if (sortMethod != null) { + if (sortMethod.equals(FileUtils.SORT_LAST_MODIFIED)) { + this.mComparator = this.mLastModifiedComparator; + } + } + if (getListAdapter() == null) { // Assign the list adapter to the ListView setListAdapter(new FileListAdapter(this)); diff --git a/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java b/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java index dfc8171..af6ab67 100644 --- a/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java +++ b/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java @@ -57,6 +57,8 @@ public class FileUtils { public static final String MIME_TYPE_APP = "application/*"; public static final String EXTRA_MIME_TYPES = "net.zhuoweizhang.afilechooser.extra.MIME_TYPES"; + public static final String EXTRA_SORT_METHOD = "net.zhuoweizhang.afilechooser.extra.SORT_METHOD"; + public static final String SORT_LAST_MODIFIED = "net.zhuoweizhang.afilechooser.extra.SORT_LAST_MODIFIED"; /** * Whether the filename is a video file. From 8d9e9989f46f9ce594add8b9945464791362b9af Mon Sep 17 00:00:00 2001 From: Zhuowei Zhang Date: Wed, 20 Aug 2014 22:37:26 -0700 Subject: [PATCH 6/6] Move the loading of the extra mime types to before the first scan of the directory --- .../ipaulpro/afilechooser/FileChooserActivity.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java b/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java index 7f89eb4..7e4cc7b 100644 --- a/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java +++ b/aFileChooser/src/com/ipaulpro/afilechooser/FileChooserActivity.java @@ -326,6 +326,13 @@ protected void onFileDisconnect(){ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + String[] extraMimeTypes = getIntent().getStringArrayExtra(FileUtils.EXTRA_MIME_TYPES); + extendedMimeTypes.clear(); + if (extraMimeTypes != null) { + for (String s: extraMimeTypes) + extendedMimeTypes.add(s); + } + // Get the external storage directory. this.mExternalDir = Environment.getExternalStorageDirectory(); @@ -356,12 +363,6 @@ protected void onCreate(Bundle savedInstanceState) { fillList(0); } } - String[] extraMimeTypes = getIntent().getStringArrayExtra(FileUtils.EXTRA_MIME_TYPES); - extendedMimeTypes.clear(); - if (extraMimeTypes != null) { - for (String s: extraMimeTypes) - extendedMimeTypes.add(s); - } } @Override