aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/cyanogenmod/filemanager
diff options
context:
space:
mode:
authorJo De Boeck <deboeck.jo@gmail.com>2013-09-12 21:47:44 +0200
committerDanny Baumann <dannybaumann@web.de>2013-09-20 14:55:51 +0200
commit2f9277f279882b0ed4d30b84bf7327610b2a84e6 (patch)
treee88b8ef5519c1029d23dae25ddd64eab5d827c09 /src/com/cyanogenmod/filemanager
parent2f2d3e6a6f4fd74d9ef0c11420243d638124d13a (diff)
downloadandroid_packages_apps_CMFileManager-2f9277f279882b0ed4d30b84bf7327610b2a84e6.tar.gz
android_packages_apps_CMFileManager-2f9277f279882b0ed4d30b84bf7327610b2a84e6.tar.bz2
android_packages_apps_CMFileManager-2f9277f279882b0ed4d30b84bf7327610b2a84e6.zip
Implement showing of apk icon and image and video thumbs in listing
Patchset 8: Move some stuff to MimeTypeHelper Add cache Add image and thumbail support Add setting for this (defaul false for improve performance) Patchset 9: Fixed icon derp display Same size for all drawables Patchset 10: Async drawable load Shrink cache Patchset 11: Use layout's width and height to scale the images Patchset 12: Music album thumbs Patchset 13: Added suggestions Fixed FC Patchset 14: Use content observer for icons Keep context in icon holder Fix scroll position being reset on thumbnail load Patchset 15: Additional fixes and optimizations Patchset 16: Do not saturate the ui with constants redrawing Do not send back events while changing to a new directory Fix FC on HistoryActivity (creation of a ContentObserver inside of a non-ui thread) Added normalized media paths Patchset 17: Use a HandlerThread to handle the preview loads Patchset 18: Optimizations Patchset 19: Fix deadlock Patchset 20: Remove debug leftover Patchset 21: Fix whitespace Patchset 22: Misc. cleanup Change-Id: I0a4ea801ff0cfbf31dda125b8925e7d35f4c3c99
Diffstat (limited to 'src/com/cyanogenmod/filemanager')
-rw-r--r--src/com/cyanogenmod/filemanager/activities/HistoryActivity.java20
-rw-r--r--src/com/cyanogenmod/filemanager/activities/NavigationActivity.java19
-rw-r--r--src/com/cyanogenmod/filemanager/activities/SearchActivity.java17
-rw-r--r--src/com/cyanogenmod/filemanager/activities/preferences/GeneralPreferenceFragment.java7
-rw-r--r--src/com/cyanogenmod/filemanager/adapters/BookmarksAdapter.java20
-rw-r--r--src/com/cyanogenmod/filemanager/adapters/FileSystemObjectAdapter.java58
-rw-r--r--src/com/cyanogenmod/filemanager/adapters/HistoryAdapter.java20
-rw-r--r--src/com/cyanogenmod/filemanager/adapters/SearchResultAdapter.java46
-rw-r--r--src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java6
-rw-r--r--src/com/cyanogenmod/filemanager/ui/IconHolder.java303
-rw-r--r--src/com/cyanogenmod/filemanager/ui/widgets/FixedSizeImageView.java68
-rw-r--r--src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java344
-rw-r--r--src/com/cyanogenmod/filemanager/util/MediaHelper.java131
-rw-r--r--src/com/cyanogenmod/filemanager/util/MimeTypeHelper.java40
14 files changed, 876 insertions, 223 deletions
diff --git a/src/com/cyanogenmod/filemanager/activities/HistoryActivity.java b/src/com/cyanogenmod/filemanager/activities/HistoryActivity.java
index 817028f6..964d880f 100644
--- a/src/com/cyanogenmod/filemanager/activities/HistoryActivity.java
+++ b/src/com/cyanogenmod/filemanager/activities/HistoryActivity.java
@@ -220,33 +220,30 @@ public class HistoryActivity extends Activity implements OnItemClickListener {
this.mListView = (ListView)findViewById(R.id.history_listview);
// Load the history in background
- AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
+ AsyncTask<Void, Void, List<History>> task = new AsyncTask<Void, Void, List<History>>() {
Exception mCause;
List<History> mHistory;
@Override
- protected Boolean doInBackground(Void... params) {
+ protected List<History> doInBackground(Void... params) {
try {
this.mHistory =
(List<History>)getIntent().getSerializableExtra(EXTRA_HISTORY_LIST);
if (this.mHistory.isEmpty()) {
View msg = findViewById(R.id.history_empty_msg);
msg.setVisibility(View.VISIBLE);
- return Boolean.TRUE;
+ return new ArrayList<History>();
}
HistoryActivity.this.mIsEmpty = this.mHistory.isEmpty();
//Show inverted history
final List<History> adapterList = new ArrayList<History>(this.mHistory);
Collections.reverse(adapterList);
- HistoryActivity.this.mAdapter =
- new HistoryAdapter(HistoryActivity.this, adapterList);
-
- return Boolean.TRUE;
+ return adapterList;
} catch (Exception e) {
this.mCause = e;
- return Boolean.FALSE;
+ return null;
}
}
@@ -256,9 +253,12 @@ public class HistoryActivity extends Activity implements OnItemClickListener {
}
@Override
- protected void onPostExecute(Boolean result) {
+ protected void onPostExecute(List<History> result) {
waiting.setVisibility(View.GONE);
- if (result.booleanValue()) {
+ if (result != null) {
+ HistoryActivity.this.mAdapter =
+ new HistoryAdapter(HistoryActivity.this, result);
+
if (HistoryActivity.this.mListView != null &&
HistoryActivity.this.mAdapter != null) {
diff --git a/src/com/cyanogenmod/filemanager/activities/NavigationActivity.java b/src/com/cyanogenmod/filemanager/activities/NavigationActivity.java
index a837c4b8..3d97a04a 100644
--- a/src/com/cyanogenmod/filemanager/activities/NavigationActivity.java
+++ b/src/com/cyanogenmod/filemanager/activities/NavigationActivity.java
@@ -207,6 +207,14 @@ public class NavigationActivity extends Activity
return;
}
+ // Display thumbs
+ if (key.compareTo(FileManagerSettings.
+ SETTINGS_DISPLAY_THUMBS.getId()) == 0) {
+ // Clean the icon cache applying the current theme
+ applyTheme();
+ return;
+ }
+
// Use flinger
if (key.compareTo(FileManagerSettings.
SETTINGS_USE_FLINGER.getId()) == 0) {
@@ -1308,7 +1316,7 @@ public class NavigationActivity extends Activity
* @param history The history reference
* @return boolean A problem occurs while navigate
*/
- public boolean navigateToHistory(History history) {
+ public synchronized boolean navigateToHistory(History history) {
try {
//Gets the history
History realHistory = this.mHistory.get(history.getPosition());
@@ -1322,7 +1330,9 @@ public class NavigationActivity extends Activity
NavigationView view = getNavigationView(viewId);
// Selected items must not be restored from on history navigation
info.setSelectedFiles(view.getSelectedFiles());
- view.onRestoreState(info);
+ if (!view.onRestoreState(info)) {
+ return true;
+ }
} else if (realHistory.getItem() instanceof SearchInfoParcelable) {
//Search (open search with the search results)
@@ -1598,6 +1608,11 @@ public class NavigationActivity extends Activity
* @hide
*/
void exit() {
+ // Recycle the navigation views
+ int cc = this.mNavigationViews.length;
+ for (int i = 0; i < cc; i++) {
+ this.mNavigationViews[i].recycle();
+ }
try {
FileManagerApplication.destroyBackgroundConsole();
} catch (Throwable ex) {
diff --git a/src/com/cyanogenmod/filemanager/activities/SearchActivity.java b/src/com/cyanogenmod/filemanager/activities/SearchActivity.java
index 75d528e4..a50a1a08 100644
--- a/src/com/cyanogenmod/filemanager/activities/SearchActivity.java
+++ b/src/com/cyanogenmod/filemanager/activities/SearchActivity.java
@@ -611,6 +611,9 @@ public class SearchActivity extends Activity
}
//Set the listview
+ if (this.mSearchListView.getAdapter() != null) {
+ ((SearchResultAdapter)this.mSearchListView.getAdapter()).dispose();
+ }
this.mResultList = new ArrayList<FileSystemObject>();
SearchResultAdapter adapter =
new SearchResultAdapter(this,
@@ -1047,7 +1050,7 @@ public class SearchActivity extends Activity
@Override
public void onSuccess() {
if (navigateTo(fFso, intent)) {
- finish();
+ exit();
}
}
@Override
@@ -1072,8 +1075,18 @@ public class SearchActivity extends Activity
// End this activity
if (finish) {
- finish();
+ exit();
+ }
+ }
+
+ /**
+ * Method invoked when the activity needs to exit
+ */
+ private void exit() {
+ if (this.mSearchListView.getAdapter() != null) {
+ ((SearchResultAdapter)this.mSearchListView.getAdapter()).dispose();
}
+ finish();
}
/**
diff --git a/src/com/cyanogenmod/filemanager/activities/preferences/GeneralPreferenceFragment.java b/src/com/cyanogenmod/filemanager/activities/preferences/GeneralPreferenceFragment.java
index 8d1385d9..16e3eabf 100644
--- a/src/com/cyanogenmod/filemanager/activities/preferences/GeneralPreferenceFragment.java
+++ b/src/com/cyanogenmod/filemanager/activities/preferences/GeneralPreferenceFragment.java
@@ -47,6 +47,7 @@ public class GeneralPreferenceFragment extends TitlePreferenceFragment {
private ListPreference mFiletimeFormatMode;
private ListPreference mFreeDiskSpaceWarningLevel;
private CheckBoxPreference mComputeFolderStatistics;
+ private CheckBoxPreference mDisplayThumbs;
// private CheckBoxPreference mUseFlinger;
private ListPreference mAccessMode;
private CheckBoxPreference mDebugTraces;
@@ -182,6 +183,12 @@ public class GeneralPreferenceFragment extends TitlePreferenceFragment {
FileManagerSettings.SETTINGS_COMPUTE_FOLDER_STATISTICS.getId());
this.mComputeFolderStatistics.setOnPreferenceChangeListener(this.mOnChangeListener);
+ // Display thumbs
+ this.mDisplayThumbs =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getId());
+ this.mDisplayThumbs.setOnPreferenceChangeListener(this.mOnChangeListener);
+
// Use flinger
// this.mUseFlinger =
// (CheckBoxPreference)findPreference(
diff --git a/src/com/cyanogenmod/filemanager/adapters/BookmarksAdapter.java b/src/com/cyanogenmod/filemanager/adapters/BookmarksAdapter.java
index cbe63a49..bbb5e3c8 100644
--- a/src/com/cyanogenmod/filemanager/adapters/BookmarksAdapter.java
+++ b/src/com/cyanogenmod/filemanager/adapters/BookmarksAdapter.java
@@ -104,7 +104,7 @@ public class BookmarksAdapter extends ArrayAdapter<Bookmark> {
public BookmarksAdapter(
Context context, List<Bookmark> bookmarks, OnClickListener onActionClickListener) {
super(context, RESOURCE_ITEM_NAME, bookmarks);
- this.mIconHolder = new IconHolder();
+ this.mIconHolder = new IconHolder(context, false);
this.mOnActionClickListener = onActionClickListener;
//Do cache of the data for better performance
@@ -126,7 +126,10 @@ public class BookmarksAdapter extends ArrayAdapter<Bookmark> {
public void dispose() {
clear();
this.mData = null;
- this.mIconHolder = null;
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ mIconHolder = null;
+ }
}
/**
@@ -144,21 +147,19 @@ public class BookmarksAdapter extends ArrayAdapter<Bookmark> {
//Build the data holder
this.mData[i] = new BookmarksAdapter.DataHolder();
this.mData[i].mDwIcon =
- this.mIconHolder.getDrawable(getContext(), BookmarksHelper.getIcon(bookmark));
+ this.mIconHolder.getDrawable(BookmarksHelper.getIcon(bookmark));
this.mData[i].mName = bookmark.mName;
this.mData[i].mPath = bookmark.mPath;
this.mData[i].mDwAction = null;
this.mData[i].mActionCd = null;
if (bookmark.mType.compareTo(BOOKMARK_TYPE.HOME) == 0) {
this.mData[i].mDwAction =
- this.mIconHolder.getDrawable(
- getContext(), "ic_config_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_config_drawable"); //$NON-NLS-1$
this.mData[i].mActionCd =
getContext().getString(R.string.bookmarks_button_config_cd);
} else if (bookmark.mType.compareTo(BOOKMARK_TYPE.USER_DEFINED) == 0) {
this.mData[i].mDwAction =
- this.mIconHolder.getDrawable(getContext(),
- "ic_close_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_close_drawable"); //$NON-NLS-1$
this.mData[i].mActionCd =
getContext().getString(R.string.bookmarks_button_remove_bookmark_cd);
}
@@ -220,7 +221,10 @@ public class BookmarksAdapter extends ArrayAdapter<Bookmark> {
* Method that should be invoked when the theme of the app was changed
*/
public void notifyThemeChanged() {
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ }
// Empty icon holder
- this.mIconHolder = new IconHolder();
+ this.mIconHolder = new IconHolder(getContext(), false);
}
}
diff --git a/src/com/cyanogenmod/filemanager/adapters/FileSystemObjectAdapter.java b/src/com/cyanogenmod/filemanager/adapters/FileSystemObjectAdapter.java
index eeb70bf0..333ac13e 100644
--- a/src/com/cyanogenmod/filemanager/adapters/FileSystemObjectAdapter.java
+++ b/src/com/cyanogenmod/filemanager/adapters/FileSystemObjectAdapter.java
@@ -31,6 +31,8 @@ import android.widget.TextView;
import com.cyanogenmod.filemanager.R;
import com.cyanogenmod.filemanager.model.FileSystemObject;
import com.cyanogenmod.filemanager.model.ParentDirectory;
+import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
+import com.cyanogenmod.filemanager.preferences.Preferences;
import com.cyanogenmod.filemanager.ui.IconHolder;
import com.cyanogenmod.filemanager.ui.ThemeManager;
import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
@@ -94,6 +96,7 @@ public class FileSystemObjectAdapter
String mSize;
}
+ private static final int MESSAGE_REDRAW = 1;
private DataHolder[] mData;
private IconHolder mIconHolder;
@@ -103,6 +106,8 @@ public class FileSystemObjectAdapter
private OnSelectionChangedListener mOnSelectionChangedListener;
+ private boolean mDisposed;
+
//The resource of the item check
private static final int RESOURCE_ITEM_CHECK = R.id.navigation_view_item_check;
//The resource of the item icon
@@ -127,14 +132,13 @@ public class FileSystemObjectAdapter
Context context, List<FileSystemObject> files,
int itemViewResourceId, boolean pickable) {
super(context, RESOURCE_ITEM_NAME, files);
- this.mIconHolder = new IconHolder();
+ this.mDisposed = false;
this.mItemViewResourceId = itemViewResourceId;
this.mSelectedItems = new ArrayList<FileSystemObject>();
this.mPickable = pickable;
+ notifyThemeChanged(); // Reload icons
- //Do cache of the data for better performance
- loadDefaultIcons();
- processData(files);
+ processData();
}
/**
@@ -151,8 +155,8 @@ public class FileSystemObjectAdapter
* Method that loads the default icons (known icons and more common icons).
*/
private void loadDefaultIcons() {
- this.mIconHolder.getDrawable(getContext(), "ic_fso_folder_drawable"); //$NON-NLS-1$
- this.mIconHolder.getDrawable(getContext(), "ic_fso_default_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_fso_folder_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_fso_default_drawable"); //$NON-NLS-1$
}
/**
@@ -160,7 +164,10 @@ public class FileSystemObjectAdapter
*/
@Override
public void notifyDataSetChanged() {
- processData(null);
+ if (this.mDisposed) {
+ return;
+ }
+ processData();
super.notifyDataSetChanged();
}
@@ -168,9 +175,13 @@ public class FileSystemObjectAdapter
* Method that dispose the elements of the adapter.
*/
public void dispose() {
+ this.mDisposed = true;
clear();
this.mData = null;
- this.mIconHolder = null;
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ mIconHolder = null;
+ }
this.mSelectedItems.clear();
}
@@ -194,17 +205,17 @@ public class FileSystemObjectAdapter
/**
* Method that process the data before use {@link #getView} method.
- *
- * @param files The list of files (to better performance) or null.
*/
- private void processData(List<FileSystemObject> files) {
+ private void processData() {
Theme theme = ThemeManager.getCurrentTheme(getContext());
Resources res = getContext().getResources();
- this.mData = new DataHolder[getCount()];
- int cc = (files == null) ? getCount() : files.size();
+ int cc = getCount();
+
+ this.mData = new DataHolder[cc];
+
for (int i = 0; i < cc; i++) {
//File system object info
- FileSystemObject fso = (files == null) ? getItem(i) : files.get(i);
+ FileSystemObject fso = getItem(i);
//Parse the last modification time and permissions
StringBuilder sbSummary = new StringBuilder();
@@ -231,12 +242,10 @@ public class FileSystemObjectAdapter
getContext(), "checkbox_deselected_drawable"); //$NON-NLS-1$
}
this.mData[i].mDwIcon = this.mIconHolder.getDrawable(
- getContext(),
MimeTypeHelper.getIcon(getContext(), fso));
this.mData[i].mName = fso.getName();
this.mData[i].mSummary = sbSummary.toString();
this.mData[i].mSize = FileHelper.getHumanReadableSize(fso);
-
}
}
@@ -289,7 +298,13 @@ public class FileSystemObjectAdapter
}
//Set the data
- viewHolder.mIvIcon.setImageDrawable(dataHolder.mDwIcon);
+
+ if (convertView != null) {
+ // Cancel load for previous usage
+ mIconHolder.cancelLoad(viewHolder.mIvIcon);
+ }
+ mIconHolder.loadDrawable(viewHolder.mIvIcon, getItem(position), dataHolder.mDwIcon);
+
viewHolder.mTvName.setText(dataHolder.mName);
if (viewHolder.mTvSummary != null) {
viewHolder.mTvSummary.setText(dataHolder.mSummary);
@@ -532,7 +547,14 @@ public class FileSystemObjectAdapter
*/
public void notifyThemeChanged() {
// Empty icon holder
- this.mIconHolder = new IconHolder();
+ if (this.mIconHolder != null) {
+ this.mIconHolder.cleanup();
+ }
+ final boolean displayThumbs = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getDefaultValue()).booleanValue());
+ this.mIconHolder = new IconHolder(getContext(), displayThumbs);
+ loadDefaultIcons();
}
}
diff --git a/src/com/cyanogenmod/filemanager/adapters/HistoryAdapter.java b/src/com/cyanogenmod/filemanager/adapters/HistoryAdapter.java
index 4582410f..f8ebbf02 100644
--- a/src/com/cyanogenmod/filemanager/adapters/HistoryAdapter.java
+++ b/src/com/cyanogenmod/filemanager/adapters/HistoryAdapter.java
@@ -98,7 +98,7 @@ public class HistoryAdapter extends ArrayAdapter<History> {
*/
public HistoryAdapter(Context context, List<History> history) {
super(context, RESOURCE_ITEM_NAME, history);
- this.mIconHolder = new IconHolder();
+ notifyThemeChanged(); // Reload icons
//Do cache of the data for better performance
processData(history);
@@ -119,7 +119,10 @@ public class HistoryAdapter extends ArrayAdapter<History> {
public void dispose() {
clear();
this.mData = null;
- this.mIconHolder = null;
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ mIconHolder = null;
+ }
}
/**
@@ -138,12 +141,10 @@ public class HistoryAdapter extends ArrayAdapter<History> {
this.mData[i] = new HistoryAdapter.DataHolder();
if (history.getItem() instanceof NavigationViewInfoParcelable) {
this.mData[i].mDwIcon =
- this.mIconHolder.getDrawable(
- getContext(), "ic_fso_folder_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_fso_folder_drawable"); //$NON-NLS-1$
} else if (history.getItem() instanceof SearchInfoParcelable) {
this.mData[i].mDwIcon =
- this.mIconHolder.getDrawable(
- getContext(), "ic_history_search_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_history_search_drawable"); //$NON-NLS-1$
}
this.mData[i].mName = history.getItem().getTitle();
if (this.mData[i].mName == null || this.mData[i].mName.trim().length() == 0) {
@@ -207,8 +208,11 @@ public class HistoryAdapter extends ArrayAdapter<History> {
* Method that should be invoked when the theme of the app was changed
*/
public void notifyThemeChanged() {
- // Empty icon holder
- this.mIconHolder = new IconHolder();
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ }
+ // Empty icon holder (only have folders and search icons)
+ this.mIconHolder = new IconHolder(getContext(), false);
}
}
diff --git a/src/com/cyanogenmod/filemanager/adapters/SearchResultAdapter.java b/src/com/cyanogenmod/filemanager/adapters/SearchResultAdapter.java
index 0561a647..0caf438b 100644
--- a/src/com/cyanogenmod/filemanager/adapters/SearchResultAdapter.java
+++ b/src/com/cyanogenmod/filemanager/adapters/SearchResultAdapter.java
@@ -80,6 +80,7 @@ public class SearchResultAdapter extends ArrayAdapter<SearchResult> {
Float mRelevance;
}
+ private static final int MESSAGE_REDRAW = 1;
private DataHolder[] mData;
private IconHolder mIconHolder;
@@ -90,6 +91,8 @@ public class SearchResultAdapter extends ArrayAdapter<SearchResult> {
private final List<String> mQueries;
+ private boolean mDisposed;
+
//The resource of the item icon
private static final int RESOURCE_ITEM_ICON = R.id.search_item_icon;
//The resource of the item name
@@ -111,7 +114,11 @@ public class SearchResultAdapter extends ArrayAdapter<SearchResult> {
public SearchResultAdapter(
Context context, List<SearchResult> files, int itemViewResourceId, Query queries) {
super(context, RESOURCE_ITEM_NAME, files);
- this.mIconHolder = new IconHolder();
+ this.mDisposed = false;
+ final boolean displayThumbs = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getDefaultValue()).booleanValue());
+ this.mIconHolder = new IconHolder(context, displayThumbs);
this.mItemViewResourceId = itemViewResourceId;
this.mQueries = queries.getQueries();
@@ -127,15 +134,15 @@ public class SearchResultAdapter extends ArrayAdapter<SearchResult> {
//Do cache of the data for better performance
loadDefaultIcons();
- processData(files);
+ processData();
}
/**
* Method that loads the default icons (known icons and more common icons).
*/
private void loadDefaultIcons() {
- this.mIconHolder.getDrawable(getContext(), "ic_fso_folder_drawable"); //$NON-NLS-1$
- this.mIconHolder.getDrawable(getContext(), "ic_fso_default_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_fso_folder_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_fso_default_drawable"); //$NON-NLS-1$
}
/**
@@ -143,7 +150,10 @@ public class SearchResultAdapter extends ArrayAdapter<SearchResult> {
*/
@Override
public void notifyDataSetChanged() {
- processData(null);
+ if (this.mDisposed) {
+ return;
+ }
+ processData();
super.notifyDataSetChanged();
}
@@ -151,6 +161,10 @@ public class SearchResultAdapter extends ArrayAdapter<SearchResult> {
* Method that dispose the elements of the adapter.
*/
public void dispose() {
+ if (this.mIconHolder != null) {
+ this.mIconHolder.cleanup();
+ }
+ this.mDisposed = true;
clear();
this.mData = null;
this.mIconHolder = null;
@@ -158,25 +172,23 @@ public class SearchResultAdapter extends ArrayAdapter<SearchResult> {
/**
* Method that process the data before use {@link #getView} method.
- *
- * @param files The list of files (to better performance) or null.
*/
- private void processData(List<SearchResult> files) {
+ private void processData() {
Theme theme = ThemeManager.getCurrentTheme(getContext());
int highlightedColor =
theme.getColor(getContext(), "search_highlight_color"); //$NON-NLS-1$
this.mData = new DataHolder[getCount()];
- int cc = (files == null) ? getCount() : files.size();
+ int cc = getCount();
for (int i = 0; i < cc; i++) {
//File system object info
- SearchResult result = (files == null) ? getItem(i) : files.get(i);
+ SearchResult result = getItem(i);
//Build the data holder
+ final FileSystemObject fso = result.getFso();
this.mData[i] = new SearchResultAdapter.DataHolder();
- this.mData[i].mDwIcon =
- this.mIconHolder.getDrawable(
- getContext(), MimeTypeHelper.getIcon(getContext(), result.getFso()));
+ this.mData[i].mDwIcon = this.mIconHolder.getDrawable(
+ MimeTypeHelper.getIcon(getContext(), fso));
if (this.mHighlightTerms) {
this.mData[i].mName =
SearchHelper.getHighlightedName(result, this.mQueries, highlightedColor);
@@ -264,7 +276,13 @@ public class SearchResultAdapter extends ArrayAdapter<SearchResult> {
ViewHolder viewHolder = (ViewHolder)v.getTag();
//Set the data
- viewHolder.mIvIcon.setImageDrawable(dataHolder.mDwIcon);
+
+ if (convertView != null) {
+ mIconHolder.cancelLoad(viewHolder.mIvIcon);
+ }
+ mIconHolder.loadDrawable(viewHolder.mIvIcon,
+ getItem(position).getFso(), dataHolder.mDwIcon);
+
viewHolder.mTvName.setText(dataHolder.mName, TextView.BufferType.SPANNABLE);
viewHolder.mTvParentDir.setText(dataHolder.mParentDir);
if (dataHolder.mRelevance != null) {
diff --git a/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java b/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java
index 5a220119..4d2bb2fa 100644
--- a/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java
+++ b/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java
@@ -98,6 +98,12 @@ public enum FileManagerSettings {
SETTINGS_COMPUTE_FOLDER_STATISTICS(
"cm_filemanager_compute_folder_statistics", Boolean.FALSE), //$NON-NLS-1$
/**
+ * When to display thumbs of pictures, videos, ...
+ * @hide
+ */
+ SETTINGS_DISPLAY_THUMBS(
+ "cm_filemanager_show_thumbs", Boolean.FALSE), //$NON-NLS-1$
+ /**
* Whether use flinger to remove items
* @hide
*/
diff --git a/src/com/cyanogenmod/filemanager/ui/IconHolder.java b/src/com/cyanogenmod/filemanager/ui/IconHolder.java
index df60a81f..2f658647 100644
--- a/src/com/cyanogenmod/filemanager/ui/IconHolder.java
+++ b/src/com/cyanogenmod/filemanager/ui/IconHolder.java
@@ -16,12 +16,31 @@
package com.cyanogenmod.filemanager.ui;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.media.ThumbnailUtils;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.widget.ImageView;
+import com.cyanogenmod.filemanager.model.FileSystemObject;
import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
+import com.cyanogenmod.filemanager.util.FileHelper;
+import com.cyanogenmod.filemanager.util.MediaHelper;
+import com.cyanogenmod.filemanager.util.MimeTypeHelper.KnownMimeTypeResolver;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -29,35 +48,303 @@ import java.util.Map;
*/
public class IconHolder {
- private final Map<String, Drawable> mIcons;
+ private static final int MAX_CACHE = 500;
+
+ private static final int MSG_LOAD = 1;
+ private static final int MSG_LOADED = 2;
+ private static final int MSG_DESTROY = 3;
+
+ private final Map<String, Drawable> mIcons; // Themes based
+ private final Map<String, Drawable> mAppIcons; // App based
+
+ private Map<String, Long> mAlbums; // Media albums
+
+ private Map<ImageView, FileSystemObject> mRequests;
+
+ private final Context mContext;
+ private final boolean mUseThumbs;
+ private boolean mNeedAlbumUpdate = true;
+
+ private HandlerThread mWorkerThread;
+ private Handler mWorkerHandler;
+
+ private static class LoadResult {
+ FileSystemObject fso;
+ Drawable result;
+ }
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_LOADED:
+ processResult((LoadResult) msg.obj);
+ sendEmptyMessageDelayed(MSG_DESTROY, 3000);
+ break;
+ case MSG_DESTROY:
+ shutdownWorker();
+ break;
+ }
+ }
+
+ private void processResult(LoadResult result) {
+ // Cache the new drawable
+ final String filePath = MediaHelper.normalizeMediaPath(result.fso.getFullPath());
+ mAppIcons.put(filePath, result.result);
+
+ // find the request for it
+ for (Map.Entry<ImageView, FileSystemObject> entry : mRequests.entrySet()) {
+ final ImageView imageView = entry.getKey();
+ final FileSystemObject fso = entry.getValue();
+ if (fso == result.fso) {
+ imageView.setImageDrawable(result.result);
+ mRequests.remove(imageView);
+ break;
+ }
+ }
+ }
+ };
+
+ private ContentObserver mMediaObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (this) {
+ mNeedAlbumUpdate = true;
+ }
+ }
+ };
/**
* Constructor of <code>IconHolder</code>.
+ *
+ * @param useThumbs If thumbs of images, videos, apps, ... should be returned
+ * instead of the default icon.
*/
- public IconHolder() {
+ public IconHolder(Context context, boolean useThumbs) {
super();
+ this.mContext = context;
+ this.mUseThumbs = useThumbs;
+ this.mRequests = new HashMap<ImageView, FileSystemObject>();
this.mIcons = new HashMap<String, Drawable>();
+ this.mAppIcons = new LinkedHashMap<String, Drawable>(MAX_CACHE, .75F, true) {
+ private static final long serialVersionUID = 1L;
+ @Override
+ protected boolean removeEldestEntry(Entry<String, Drawable> eldest) {
+ return size() > MAX_CACHE;
+ }
+ };
+ this.mAlbums = new HashMap<String, Long>();
+ if (useThumbs) {
+ final ContentResolver cr = mContext.getContentResolver();
+ for (Uri uri : MediaHelper.RELEVANT_URIS) {
+ cr.registerContentObserver(uri, true, mMediaObserver);
+ }
+ }
}
/**
- * Method that loads, cache and returns a drawable reference
- * of a icon.
+ * Method that returns a drawable reference of a icon.
*
- * @param context The current context
* @param resid The resource identifier
* @return Drawable The drawable icon reference
*/
- public Drawable getDrawable(Context context, final String resid) {
+ public Drawable getDrawable(final String resid) {
//Check if the icon exists in the cache
if (this.mIcons.containsKey(resid)) {
return this.mIcons.get(resid);
}
//Load the drawable, cache and returns reference
- Theme theme = ThemeManager.getCurrentTheme(context);
- Drawable dw = theme.getDrawable(context, resid);
+ Theme theme = ThemeManager.getCurrentTheme(mContext);
+ Drawable dw = theme.getDrawable(mContext, resid);
this.mIcons.put(resid, dw);
return dw;
}
+ /**
+ * Method that returns a drawable reference of a FileSystemObject.
+ *
+ * @param iconView View to load the drawable into
+ * @param fso The FileSystemObject reference
+ * @param defaultIcon Drawable to be used in case no specific one could be found
+ * @return Drawable The drawable reference
+ */
+ public void loadDrawable(ImageView iconView, FileSystemObject fso, Drawable defaultIcon) {
+ if (!mUseThumbs) {
+ iconView.setImageDrawable(defaultIcon);
+ return;
+ }
+
+ // Is cached?
+ final String filePath = MediaHelper.normalizeMediaPath(fso.getFullPath());
+ if (this.mAppIcons.containsKey(filePath)) {
+ iconView.setImageDrawable(this.mAppIcons.get(filePath));
+ return;
+ }
+
+ mRequests.put(iconView, fso);
+ iconView.setImageDrawable(defaultIcon);
+
+ mHandler.removeMessages(MSG_DESTROY);
+ if (mWorkerThread == null) {
+ mWorkerThread = new HandlerThread("IconHolderLoader");
+ mWorkerThread.start();
+ mWorkerHandler = new WorkerHandler(mWorkerThread.getLooper());
+ }
+ Message msg = mWorkerHandler.obtainMessage(MSG_LOAD, fso);
+ msg.sendToTarget();
+ }
+
+ /**
+ * Cancel loading of a drawable for a certain ImageView.
+ */
+ public void cancelLoad(ImageView view) {
+ FileSystemObject fso = mRequests.get(view);
+ if (fso != null && mWorkerHandler != null) {
+ mWorkerHandler.removeMessages(MSG_LOAD, fso);
+ }
+ mRequests.remove(view);
+ }
+
+ private class WorkerHandler extends Handler {
+ public WorkerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_LOAD:
+ FileSystemObject fso = (FileSystemObject) msg.obj;
+ Drawable d = loadDrawable(fso);
+ if (d != null) {
+ LoadResult result = new LoadResult();
+ result.fso = fso;
+ result.result = d;
+ mHandler.obtainMessage(MSG_LOADED, result).sendToTarget();
+ }
+ break;
+ }
+ }
+
+ private Drawable loadDrawable(FileSystemObject fso) {
+ final String filePath = MediaHelper.normalizeMediaPath(fso.getFullPath());
+
+ if (KnownMimeTypeResolver.isAndroidApp(mContext, fso)) {
+ return getAppDrawable(fso);
+ } else if (KnownMimeTypeResolver.isImage(mContext, fso)) {
+ return getImageDrawable(filePath);
+ } else if (KnownMimeTypeResolver.isVideo(mContext, fso)) {
+ return getVideoDrawable(filePath);
+ } else if (FileHelper.isDirectory(fso)) {
+ synchronized (mMediaObserver) {
+ if (mNeedAlbumUpdate) {
+ mNeedAlbumUpdate = false;
+ mAlbums = MediaHelper.getAllAlbums(mContext.getContentResolver());
+ }
+ }
+ if (mAlbums.containsKey(filePath)) {
+ return getAlbumDrawable(mAlbums.get(filePath));
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Method that returns the main icon of the app
+ *
+ * @param fso The FileSystemObject
+ * @return Drawable The drawable or null if cannot be extracted
+ */
+ private Drawable getAppDrawable(FileSystemObject fso) {
+ final String filepath = fso.getFullPath();
+ PackageManager pm = mContext.getPackageManager();
+ PackageInfo packageInfo = pm.getPackageArchiveInfo(filepath,
+ PackageManager.GET_ACTIVITIES);
+ if (packageInfo != null) {
+ // Read http://code.google.com/p/android/issues/detail?id=9151, CM fixed this
+ // issue. We retain it for compatibility with older versions and roms without
+ // this fix. Required to access apk which are not installed.
+ final ApplicationInfo appInfo = packageInfo.applicationInfo;
+ appInfo.sourceDir = filepath;
+ appInfo.publicSourceDir = filepath;
+ return pm.getDrawable(appInfo.packageName, appInfo.icon, appInfo);
+ }
+ return null;
+ }
+
+ /**
+ * Method that returns a thumbnail of the picture
+ *
+ * @param file The path to the file
+ * @return Drawable The drawable or null if cannot be extracted
+ */
+ private Drawable getImageDrawable(String file) {
+ Bitmap thumb = ThumbnailUtils.createImageThumbnail(
+ MediaHelper.normalizeMediaPath(file),
+ ThumbnailUtils.TARGET_SIZE_MICRO_THUMBNAIL);
+ if (thumb == null) {
+ return null;
+ }
+ return new BitmapDrawable(mContext.getResources(), thumb);
+ }
+
+ /**
+ * Method that returns a thumbnail of the video
+ *
+ * @param file The path to the file
+ * @return Drawable The drawable or null if cannot be extracted
+ */
+ private Drawable getVideoDrawable(String file) {
+ Bitmap thumb = ThumbnailUtils.createVideoThumbnail(
+ MediaHelper.normalizeMediaPath(file),
+ ThumbnailUtils.TARGET_SIZE_MICRO_THUMBNAIL);
+ if (thumb == null) {
+ return null;
+ }
+ return new BitmapDrawable(mContext.getResources(), thumb);
+ }
+
+ /**
+ * Method that returns a thumbnail of the album folder
+ *
+ * @param albumId The album identifier
+ * @return Drawable The drawable or null if cannot be extracted
+ */
+ private Drawable getAlbumDrawable(long albumId) {
+ String path = MediaHelper.getAlbumThumbnailPath(mContext.getContentResolver(), albumId);
+ if (path == null) {
+ return null;
+ }
+ Bitmap thumb = ThumbnailUtils.createImageThumbnail(path,
+ ThumbnailUtils.TARGET_SIZE_MICRO_THUMBNAIL);
+ if (thumb == null) {
+ return null;
+ }
+ return new BitmapDrawable(mContext.getResources(), thumb);
+ }
+ }
+
+ /**
+ * Shut down worker thread
+ */
+ private void shutdownWorker() {
+ if (mWorkerThread != null) {
+ mWorkerThread.getLooper().quit();
+ mWorkerHandler = null;
+ mWorkerThread = null;
+ }
+ }
+
+ /**
+ * Free any resources used by this instance
+ */
+ public void cleanup() {
+ this.mRequests.clear();
+ this.mIcons.clear();
+ this.mAppIcons.clear();
+ mContext.getContentResolver().unregisterContentObserver(mMediaObserver);
+ shutdownWorker();
+ }
}
diff --git a/src/com/cyanogenmod/filemanager/ui/widgets/FixedSizeImageView.java b/src/com/cyanogenmod/filemanager/ui/widgets/FixedSizeImageView.java
new file mode 100644
index 00000000..f2af92c6
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/ui/widgets/FixedSizeImageView.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cyanogenmod.filemanager.ui.widgets;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * A subclass of ImageView that assumes to be fixed size
+ * (not wrap_content / match_parent). Doing so it can
+ * optimize the drawable change code paths.
+ */
+public class FixedSizeImageView extends ImageView {
+ private boolean mSuppressLayoutRequest;
+
+ public FixedSizeImageView(Context context) {
+ super(context);
+ }
+
+ public FixedSizeImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FixedSizeImageView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setImageResource(int resId) {
+ mSuppressLayoutRequest = true;
+ super.setImageResource(resId);
+ mSuppressLayoutRequest = false;
+ }
+
+ public void setImageURI(Uri uri) {
+ mSuppressLayoutRequest = true;
+ super.setImageURI(uri);
+ mSuppressLayoutRequest = false;
+ }
+
+ public void setImageDrawable(Drawable drawable) {
+ mSuppressLayoutRequest = true;
+ super.setImageDrawable(drawable);
+ mSuppressLayoutRequest = false;
+ }
+
+ public void requestLayout() {
+ if (!mSuppressLayoutRequest) {
+ super.requestLayout();
+ }
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java b/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java
index 11570b43..58602a82 100644
--- a/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java
+++ b/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java
@@ -212,6 +212,7 @@ public class NavigationView extends RelativeLayout implements
List<FileSystemObject> mFiles;
private FileSystemObjectAdapter mAdapter;
+ private boolean mChangingDir;
private final Object mSync = new Object();
private OnHistoryListener mOnHistoryListener;
@@ -310,8 +311,15 @@ public class NavigationView extends RelativeLayout implements
* Invoked when the instance need to be restored.
*
* @param info The serialized info
+ * @return boolean If can restore
*/
- public void onRestoreState(NavigationViewInfoParcelable info) {
+ public boolean onRestoreState(NavigationViewInfoParcelable info) {
+ synchronized (mSync) {
+ if (mChangingDir) {
+ return false;
+ }
+ }
+
//Restore the data
this.mId = info.getId();
this.mCurrentDir = info.getCurrentDir();
@@ -321,6 +329,7 @@ public class NavigationView extends RelativeLayout implements
//Update the views
refresh();
+ return true;
}
/**
@@ -585,11 +594,20 @@ public class NavigationView extends RelativeLayout implements
}
/**
+ * Method that recycles this object
+ */
+ public void recycle() {
+ if (this.mAdapter != null) {
+ this.mAdapter.dispose();
+ }
+ }
+
+ /**
* Method that change the view mode.
*
* @param newMode The new mode
*/
- @SuppressWarnings({ "unchecked", "null" })
+ @SuppressWarnings("unchecked")
public void changeViewMode(final NavigationLayoutMode newMode) {
synchronized (this.mSync) {
//Check that it is really necessary change the mode
@@ -777,181 +795,201 @@ public class NavigationView extends RelativeLayout implements
// is created)
final String fNewDir = checkChRootedNavigation(newDir);
+ // Wait to finalization
synchronized (this.mSync) {
- //Check that it is really necessary change the directory
- if (!reload && this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0) {
- return;
+ if (mChangingDir) {
+ try {
+ mSync.wait();
+ } catch (InterruptedException iex) {
+ // Ignore
+ }
}
+ mChangingDir = true;
+ }
- final boolean hasChanged =
- !(this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0);
- final boolean isNewHistory = (this.mCurrentDir != null);
-
- //Execute the listing in a background process
- AsyncTask<String, Integer, List<FileSystemObject>> task =
- new AsyncTask<String, Integer, List<FileSystemObject>>() {
- /**
- * {@inheritDoc}
- */
- @Override
- protected List<FileSystemObject> doInBackground(String... params) {
- try {
- //Reset the custom title view and returns to breadcrumb
- if (NavigationView.this.mTitle != null) {
- NavigationView.this.mTitle.post(new Runnable() {
- @Override
- public void run() {
- try {
- NavigationView.this.mTitle.restoreView();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- }
-
-
- //Start of loading data
- if (NavigationView.this.mBreadcrumb != null) {
- try {
- NavigationView.this.mBreadcrumb.startLoading();
- } catch (Throwable ex) {
- /**NON BLOCK**/
- }
- }
+ //Check that it is really necessary change the directory
+ if (!reload && this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0) {
+ return;
+ }
- //Get the files, resolve links and apply configuration
- //(sort, hidden, ...)
- List<FileSystemObject> files = NavigationView.this.mFiles;
- if (!useCurrent) {
- files = CommandHelper.listFiles(getContext(), fNewDir, null);
- }
- return files;
- } catch (final ConsoleAllocException e) {
- //Show exception and exists
- NavigationView.this.post(new Runnable() {
+ final boolean hasChanged =
+ !(this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0);
+ final boolean isNewHistory = (this.mCurrentDir != null);
+
+ //Execute the listing in a background process
+ AsyncTask<String, Integer, List<FileSystemObject>> task =
+ new AsyncTask<String, Integer, List<FileSystemObject>>() {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected List<FileSystemObject> doInBackground(String... params) {
+ try {
+ //Reset the custom title view and returns to breadcrumb
+ if (NavigationView.this.mTitle != null) {
+ NavigationView.this.mTitle.post(new Runnable() {
@Override
public void run() {
- Context ctx = getContext();
- Log.e(TAG, ctx.getString(
- R.string.msgs_cant_create_console), e);
- DialogHelper.showToast(ctx,
- R.string.msgs_cant_create_console,
- Toast.LENGTH_LONG);
- ((Activity)ctx).finish();
+ try {
+ NavigationView.this.mTitle.restoreView();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
});
- return null;
-
- } catch (Exception ex) {
- //End of loading data
- if (NavigationView.this.mBreadcrumb != null) {
- try {
- NavigationView.this.mBreadcrumb.endLoading();
- } catch (Throwable ex2) {
- /**NON BLOCK**/
- }
+ }
+
+
+ //Start of loading data
+ if (NavigationView.this.mBreadcrumb != null) {
+ try {
+ NavigationView.this.mBreadcrumb.startLoading();
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
}
+ }
- //Capture exception (attach task, and use listener to do the anim)
- ExceptionUtil.attachAsyncTask(
- ex,
- new AsyncTask<Object, Integer, Boolean>() {
- private List<FileSystemObject> mTaskFiles = null;
- @Override
- @SuppressWarnings({
- "unchecked", "unqualified-field-access"
- })
- protected Boolean doInBackground(Object... taskParams) {
- mTaskFiles = (List<FileSystemObject>)taskParams[0];
- return Boolean.TRUE;
- }
+ //Get the files, resolve links and apply configuration
+ //(sort, hidden, ...)
+ List<FileSystemObject> files = NavigationView.this.mFiles;
+ if (!useCurrent) {
+ files = CommandHelper.listFiles(getContext(), fNewDir, null);
+ }
+ return files;
- @Override
- @SuppressWarnings("unqualified-field-access")
- protected void onPostExecute(Boolean result) {
- if (!result.booleanValue()) {
- return;
- }
- onPostExecuteTask(
- mTaskFiles, addToHistory,
- isNewHistory, hasChanged,
- searchInfo, fNewDir, scrollTo);
- }
- });
- final OnRelaunchCommandResult exListener =
- new OnRelaunchCommandResult() {
+ } catch (final ConsoleAllocException e) {
+ //Show exception and exists
+ NavigationView.this.post(new Runnable() {
+ @Override
+ public void run() {
+ Context ctx = getContext();
+ Log.e(TAG, ctx.getString(
+ R.string.msgs_cant_create_console), e);
+ DialogHelper.showToast(ctx,
+ R.string.msgs_cant_create_console,
+ Toast.LENGTH_LONG);
+ ((Activity)ctx).finish();
+ }
+ });
+ return null;
+
+ } catch (Exception ex) {
+ //End of loading data
+ if (NavigationView.this.mBreadcrumb != null) {
+ try {
+ NavigationView.this.mBreadcrumb.endLoading();
+ } catch (Throwable ex2) {
+ /**NON BLOCK**/
+ }
+ }
+
+ //Capture exception (attach task, and use listener to do the anim)
+ ExceptionUtil.attachAsyncTask(
+ ex,
+ new AsyncTask<Object, Integer, Boolean>() {
+ private List<FileSystemObject> mTaskFiles = null;
@Override
- public void onSuccess() {
- // Do animation
- fadeEfect(false);
+ @SuppressWarnings({
+ "unchecked", "unqualified-field-access"
+ })
+ protected Boolean doInBackground(Object... taskParams) {
+ mTaskFiles = (List<FileSystemObject>)taskParams[0];
+ return Boolean.TRUE;
}
+
@Override
- public void onFailed(Throwable cause) {
- // Do animation
- fadeEfect(false);
+ @SuppressWarnings("unqualified-field-access")
+ protected void onPostExecute(Boolean result) {
+ if (!result.booleanValue()) {
+ return;
+ }
+ onPostExecuteTask(
+ mTaskFiles, addToHistory,
+ isNewHistory, hasChanged,
+ searchInfo, fNewDir, scrollTo);
}
- @Override
- public void onCancelled() {
- // Do animation
- fadeEfect(false);
+ });
+ final OnRelaunchCommandResult exListener =
+ new OnRelaunchCommandResult() {
+ @Override
+ public void onSuccess() {
+ done();
+ }
+ @Override
+ public void onFailed(Throwable cause) {
+ done();
+ }
+ @Override
+ public void onCancelled() {
+ done();
+ }
+ private void done() {
+ // Do animation
+ fadeEfect(false);
+ synchronized (mSync) {
+ mChangingDir = false;
+ mSync.notify();
}
- };
- ExceptionUtil.translateException(
- getContext(), ex, false, true, exListener);
- }
- return null;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onPreExecute() {
- // Do animation
- fadeEfect(true);
+ }
+ };
+ ExceptionUtil.translateException(
+ getContext(), ex, false, true, exListener);
}
+ return null;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onPreExecute() {
+ // Do animation
+ fadeEfect(true);
+ }
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onPostExecute(List<FileSystemObject> files) {
+ // This means an exception. This method will be recalled then
+ if (files != null) {
+ onPostExecuteTask(
+ files, addToHistory, isNewHistory,
+ hasChanged, searchInfo, fNewDir, scrollTo);
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onPostExecute(List<FileSystemObject> files) {
- if (files != null) {
- onPostExecuteTask(
- files, addToHistory, isNewHistory,
- hasChanged, searchInfo, fNewDir, scrollTo);
+ // Do animation
+ fadeEfect(false);
- // Do animation
- fadeEfect(false);
+ synchronized (mSync) {
+ mChangingDir = false;
+ mSync.notify();
}
}
+ }
- /**
- * Method that performs a fade animation.
- *
- * @param out Fade out (true); Fade in (false)
- */
- void fadeEfect(final boolean out) {
- Activity activity = (Activity)getContext();
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Animation fadeAnim = out ?
- new AlphaAnimation(1, 0) :
- new AlphaAnimation(0, 1);
- fadeAnim.setDuration(50L);
- fadeAnim.setFillAfter(true);
- fadeAnim.setInterpolator(new AccelerateInterpolator());
- NavigationView.this.startAnimation(fadeAnim);
- }
- });
- }
- };
- task.execute(fNewDir);
- }
+ /**
+ * Method that performs a fade animation.
+ *
+ * @param out Fade out (true); Fade in (false)
+ */
+ void fadeEfect(final boolean out) {
+ Activity activity = (Activity)getContext();
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Animation fadeAnim = out ?
+ new AlphaAnimation(1, 0) :
+ new AlphaAnimation(0, 1);
+ fadeAnim.setDuration(50L);
+ fadeAnim.setFillAfter(true);
+ fadeAnim.setInterpolator(new AccelerateInterpolator());
+ NavigationView.this.startAnimation(fadeAnim);
+ }
+ });
+ }
+ };
+ task.execute(fNewDir);
}
diff --git a/src/com/cyanogenmod/filemanager/util/MediaHelper.java b/src/com/cyanogenmod/filemanager/util/MediaHelper.java
new file mode 100644
index 00000000..db818db3
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/util/MediaHelper.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cyanogenmod.filemanager.util;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.BaseColumns;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A helper class with useful methods to extract media data.
+ */
+public final class MediaHelper {
+
+ /**
+ * URIs that are relevant for determining album art;
+ * useful for content observer registration
+ */
+ public static final Uri[] RELEVANT_URIS = new Uri[] {
+ MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI
+ };
+
+ /**
+ * Method that returns an array with all the unique albums paths and ids.
+ *
+ * @param cr The ContentResolver
+ * @return Map<String, Long> The albums map
+ */
+ public static Map<String, Long> getAllAlbums(ContentResolver cr) {
+ Map<String, Long> albums = new HashMap<String, Long>();
+ final String[] projection =
+ {
+ "distinct " + MediaStore.Audio.Media.ALBUM_ID,
+ "substr(" + MediaStore.Audio.Media.DATA + ", 0, length(" +
+ MediaStore.Audio.Media.DATA + ") - length(" +
+ MediaStore.Audio.Media.DISPLAY_NAME + "))"
+ };
+ final String where = MediaStore.Audio.Media.IS_MUSIC + " = ?";
+ Cursor c = cr.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ projection, where, new String[]{"1"}, null);
+ if (c != null) {
+ try {
+ while (c.moveToNext()) {
+ long albumId = c.getLong(0);
+ String albumPath = c.getString(1);
+ albums.put(albumPath, albumId);
+ }
+ } finally {
+ c.close();
+ }
+ }
+ return albums;
+ }
+
+ /**
+ * Method that returns the album thumbnail path by its identifier.
+ *
+ * @param cr The ContentResolver
+ * @param albumId The album identifier to search
+ * @return String The album thumbnail path
+ */
+ public static String getAlbumThumbnailPath(ContentResolver cr, long albumId) {
+ final String[] projection = {MediaStore.Audio.Albums.ALBUM_ART};
+ final String where = BaseColumns._ID + " = ?";
+ Cursor c = cr.query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
+ projection, where, new String[]{String.valueOf(albumId)}, null);
+ try {
+ if (c != null && c.moveToNext()) {
+ return c.getString(0);
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return null;
+ }
+
+ private static final String EMULATED_STORAGE_SOURCE = System.getenv("EMULATED_STORAGE_SOURCE");
+ private static final String EMULATED_STORAGE_TARGET = System.getenv("EMULATED_STORAGE_TARGET");
+ private static final String EXTERNAL_STORAGE = System.getenv("EXTERNAL_STORAGE");
+
+ /**
+ * Method that converts a not standard media mount path to a standard media path
+ *
+ * @param path The path to normalize
+ * @return String The normalized media path
+ */
+ public static String normalizeMediaPath(String path) {
+ // Retrieve all the paths and check that we have this environment vars
+ if (TextUtils.isEmpty(EMULATED_STORAGE_SOURCE) ||
+ TextUtils.isEmpty(EMULATED_STORAGE_TARGET) ||
+ TextUtils.isEmpty(EXTERNAL_STORAGE)) {
+ return path;
+ }
+
+ // We need to convert EMULATED_STORAGE_SOURCE -> EMULATED_STORAGE_TARGET
+ if (path.startsWith(EMULATED_STORAGE_SOURCE)) {
+ path = path.replace(EMULATED_STORAGE_SOURCE, EMULATED_STORAGE_TARGET);
+ }
+ // We need to convert EXTERNAL_STORAGE -> EMULATED_STORAGE_TARGET / userId
+ if (path.startsWith(EXTERNAL_STORAGE)) {
+ final String userId = String.valueOf(UserHandle.myUserId());
+ final String target = new File(EMULATED_STORAGE_TARGET, userId).getAbsolutePath();
+ path = path.replace(EXTERNAL_STORAGE, target);
+ }
+ return path;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/util/MimeTypeHelper.java b/src/com/cyanogenmod/filemanager/util/MimeTypeHelper.java
index da37d3f5..5ddb3da6 100644
--- a/src/com/cyanogenmod/filemanager/util/MimeTypeHelper.java
+++ b/src/com/cyanogenmod/filemanager/util/MimeTypeHelper.java
@@ -553,4 +553,44 @@ public final class MimeTypeHelper {
return mimeTypeExpression.replaceAll("\\*", ".\\*"); //$NON-NLS-1$ //$NON-NLS-2$
}
+
+ /**
+ * Class for resolve known mime types
+ */
+ public static final class KnownMimeTypeResolver {
+ private static final String MIME_TYPE_APK = "application/vnd.android.package-archive";
+
+ /**
+ * Method that returns if the FileSystemObject is an Android app.
+ *
+ * @param context The current context
+ * @param fso The FileSystemObject to check
+ * @return boolean If the FileSystemObject is an Android app.
+ */
+ public static boolean isAndroidApp(Context context, FileSystemObject fso) {
+ return MIME_TYPE_APK.equals(MimeTypeHelper.getMimeType(context, fso));
+ }
+
+ /**
+ * Method that returns if the FileSystemObject is an image file.
+ *
+ * @param context The current context
+ * @param fso The FileSystemObject to check
+ * @return boolean If the FileSystemObject is an image file.
+ */
+ public static boolean isImage(Context context, FileSystemObject fso) {
+ return MimeTypeHelper.getCategory(context, fso).compareTo(MimeTypeCategory.IMAGE) == 0;
+ }
+
+ /**
+ * Method that returns if the FileSystemObject is an video file.
+ *
+ * @param context The current context
+ * @param fso The FileSystemObject to check
+ * @return boolean If the FileSystemObject is an video file.
+ */
+ public static boolean isVideo(Context context, FileSystemObject fso) {
+ return MimeTypeHelper.getCategory(context, fso).compareTo(MimeTypeCategory.VIDEO) == 0;
+ }
+ }
}