2 * Copyright (C) 2012 The CyanogenMod Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.cyanogenmod.filemanager.ui.widgets;
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.SharedPreferences;
22 import android.content.res.TypedArray;
23 import android.os.AsyncTask;
24 import android.os.storage.StorageVolume;
25 import android.util.AttributeSet;
26 import android.util.Log;
27 import android.view.View;
28 import android.view.animation.AccelerateInterpolator;
29 import android.view.animation.AlphaAnimation;
30 import android.view.animation.Animation;
31 import android.widget.AdapterView;
32 import android.widget.ListAdapter;
33 import android.widget.ListView;
34 import android.widget.RelativeLayout;
35 import android.widget.Toast;
37 import com.cyanogenmod.filemanager.FileManagerApplication;
38 import com.cyanogenmod.filemanager.R;
39 import com.cyanogenmod.filemanager.adapters.FileSystemObjectAdapter;
40 import com.cyanogenmod.filemanager.adapters.FileSystemObjectAdapter.OnSelectionChangedListener;
41 import com.cyanogenmod.filemanager.console.CancelledOperationException;
42 import com.cyanogenmod.filemanager.console.ConsoleAllocException;
43 import com.cyanogenmod.filemanager.console.VirtualMountPointConsole;
44 import com.cyanogenmod.filemanager.listeners.OnHistoryListener;
45 import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
46 import com.cyanogenmod.filemanager.listeners.OnSelectionListener;
47 import com.cyanogenmod.filemanager.model.Directory;
48 import com.cyanogenmod.filemanager.model.FileSystemObject;
49 import com.cyanogenmod.filemanager.model.ParentDirectory;
50 import com.cyanogenmod.filemanager.model.Symlink;
51 import com.cyanogenmod.filemanager.parcelables.NavigationViewInfoParcelable;
52 import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
53 import com.cyanogenmod.filemanager.preferences.AccessMode;
54 import com.cyanogenmod.filemanager.preferences.DisplayRestrictions;
55 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
56 import com.cyanogenmod.filemanager.preferences.NavigationLayoutMode;
57 import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
58 import com.cyanogenmod.filemanager.preferences.Preferences;
59 import com.cyanogenmod.filemanager.ui.ThemeManager;
60 import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
61 import com.cyanogenmod.filemanager.ui.policy.DeleteActionPolicy;
62 import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
63 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerListener;
64 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
65 import com.cyanogenmod.filemanager.util.CommandHelper;
66 import com.cyanogenmod.filemanager.util.DialogHelper;
67 import com.cyanogenmod.filemanager.util.ExceptionUtil;
68 import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
69 import com.cyanogenmod.filemanager.util.FileHelper;
70 import com.cyanogenmod.filemanager.util.StorageHelper;
73 import java.util.ArrayList;
74 import java.util.HashMap;
75 import java.util.List;
79 * The file manager implementation view (contains the graphical representation and the input
80 * management for a file manager; shows the folders/files, the mode view, react touch events,
83 public class NavigationView extends RelativeLayout implements
84 AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener,
85 BreadcrumbListener, OnSelectionChangedListener, OnSelectionListener, OnRequestRefreshListener {
87 private static final String TAG = "NavigationView"; //$NON-NLS-1$
90 * An interface to communicate selection changes events.
92 public interface OnNavigationSelectionChangedListener {
94 * Method invoked when the selection changed.
96 * @param navView The navigation view that generate the event
97 * @param selectedItems The new selected items
99 void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems);
103 * An interface to communicate a request for show the menu associated
106 public interface OnNavigationRequestMenuListener {
108 * Method invoked when a request to show the menu associated
109 * with an item is started.
111 * @param navView The navigation view that generate the event
112 * @param item The item for which the request was started
114 void onRequestMenu(NavigationView navView, FileSystemObject item);
118 * An interface to communicate a request when the user choose a file.
120 public interface OnFilePickedListener {
122 * Method invoked when a request when the user choose a file.
124 * @param item The item choose
126 void onFilePicked(FileSystemObject item);
130 * An interface to communicate a change of the current directory
132 public interface OnDirectoryChangedListener {
134 * Method invoked when the current directory changes
136 * @param item The newly active directory
138 void onDirectoryChanged(FileSystemObject item);
142 * The navigation view mode
145 public enum NAVIGATION_MODE {
147 * The navigation view acts as a browser, and allow open files itself.
151 * The navigation view acts as a picker of files
157 * A listener for flinging events from {@link FlingerListView}
159 private final OnItemFlingerListener mOnItemFlingerListener = new OnItemFlingerListener() {
162 public boolean onItemFlingerStart(
163 AdapterView<?> parent, View view, int position, long id) {
165 // Response if the item can be removed
166 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
168 // Short circuit to protect OOBE
169 if (position < 0 || position >= adapter.getCount()) {
173 FileSystemObject fso = adapter.getItem(position);
175 if (fso instanceof ParentDirectory) {
180 } catch (Exception e) {
181 ExceptionUtil.translateException(getContext(), e, true, false);
187 public void onItemFlingerEnd(OnItemFlingerResponder responder,
188 AdapterView<?> parent, View view, int position, long id) {
191 // Response if the item can be removed
192 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
193 FileSystemObject fso = adapter.getItem(position);
195 DeleteActionPolicy.removeFileSystemObject(
204 // Cancels the flinger operation
207 } catch (Exception e) {
208 ExceptionUtil.translateException(getContext(), e, true, false);
214 private class NavigationTask extends AsyncTask<String, Integer, List<FileSystemObject>> {
215 private final boolean mUseCurrent;
216 private final boolean mAddToHistory;
217 private final boolean mReload;
218 private boolean mHasChanged;
219 private boolean mIsNewHistory;
220 private String mNewDirChecked;
221 private final SearchInfoParcelable mSearchInfo;
222 private final FileSystemObject mScrollTo;
223 private final Map<DisplayRestrictions, Object> mRestrictions;
224 private final boolean mChRooted;
226 public NavigationTask(boolean useCurrent, boolean addToHistory, boolean reload,
227 SearchInfoParcelable searchInfo, FileSystemObject scrollTo,
228 Map<DisplayRestrictions, Object> restrictions, boolean chRooted) {
230 this.mUseCurrent = useCurrent;
231 this.mAddToHistory = addToHistory;
232 this.mSearchInfo = searchInfo;
233 this.mReload = reload;
234 this.mScrollTo = scrollTo;
235 this.mRestrictions = restrictions;
236 this.mChRooted = chRooted;
243 protected List<FileSystemObject> doInBackground(String... params) {
244 // Check navigation security (don't allow to go outside the ChRooted environment if one
246 mNewDirChecked = checkChRootedNavigation(params[0]);
248 mHasChanged = !(NavigationView.this.mPreviousDir != null &&
249 NavigationView.this.mPreviousDir.compareTo(mNewDirChecked) == 0);
250 mIsNewHistory = (NavigationView.this.mPreviousDir != null);
253 //Reset the custom title view and returns to breadcrumb
254 if (NavigationView.this.mTitle != null) {
255 NavigationView.this.mTitle.post(new Runnable() {
259 NavigationView.this.mTitle.restoreView();
260 } catch (Exception e) {
268 //Start of loading data
269 if (NavigationView.this.mBreadcrumb != null) {
271 NavigationView.this.mBreadcrumb.startLoading();
272 } catch (Throwable ex) {
277 //Get the files, resolve links and apply configuration
278 //(sort, hidden, ...)
279 List<FileSystemObject> files = NavigationView.this.mFiles;
281 files = CommandHelper.listFiles(getContext(), mNewDirChecked, null);
284 //Apply user preferences
285 List<FileSystemObject> sortedFiles =
286 FileHelper.applyUserPreferences(files, this.mRestrictions, this.mChRooted);
290 } catch (final ConsoleAllocException e) {
291 //Show exception and exists
292 NavigationView.this.post(new Runnable() {
295 Context ctx = getContext();
296 Log.e(TAG, ctx.getString(
297 R.string.msgs_cant_create_console), e);
298 DialogHelper.showToast(ctx,
299 R.string.msgs_cant_create_console,
301 ((Activity)ctx).finish();
305 } catch (Exception ex) {
306 //End of loading data
307 if (NavigationView.this.mBreadcrumb != null) {
309 NavigationView.this.mBreadcrumb.endLoading();
310 } catch (Throwable ex2) {
314 if (ex instanceof CancelledOperationException) {
318 //Capture exception (attach task, and use listener to do the anim)
319 ExceptionUtil.attachAsyncTask(
321 new AsyncTask<Object, Integer, Boolean>() {
322 private List<FileSystemObject> mTaskFiles = null;
325 "unchecked", "unqualified-field-access"
327 protected Boolean doInBackground(Object... taskParams) {
328 mTaskFiles = (List<FileSystemObject>)taskParams[0];
333 @SuppressWarnings("unqualified-field-access")
334 protected void onPostExecute(Boolean result) {
335 if (!result.booleanValue()) {
339 mTaskFiles, mAddToHistory, mIsNewHistory, mHasChanged,
340 mSearchInfo, mNewDirChecked, mScrollTo);
343 final OnRelaunchCommandResult exListener =
344 new OnRelaunchCommandResult() {
346 public void onSuccess() {
350 public void onFailed(Throwable cause) {
354 public void onCancelled() {
357 private void done() {
362 ExceptionUtil.translateException(
363 getContext(), ex, false, true, exListener);
372 protected void onCancelled(List<FileSystemObject> result) {
380 protected void onPostExecute(List<FileSystemObject> files) {
381 // This means an exception. This method will be recalled then
382 onPostExecuteTask(files, mAddToHistory, mIsNewHistory, mHasChanged,
383 mSearchInfo, mNewDirChecked, mScrollTo);
390 * Method that performs a fade animation.
392 * @param out Fade out (true); Fade in (false)
394 void fadeEfect(final boolean out) {
395 Activity activity = (Activity)getContext();
396 activity.runOnUiThread(new Runnable() {
399 Animation fadeAnim = out ?
400 new AlphaAnimation(1, 0) :
401 new AlphaAnimation(0, 1);
402 fadeAnim.setDuration(50L);
403 fadeAnim.setFillAfter(true);
404 fadeAnim.setInterpolator(new AccelerateInterpolator());
405 NavigationView.this.startAnimation(fadeAnim);
412 private String mCurrentDir;
413 private String mPreviousDir;
414 private NavigationLayoutMode mCurrentMode;
418 List<FileSystemObject> mFiles;
419 private FileSystemObjectAdapter mAdapter;
421 private OnHistoryListener mOnHistoryListener;
422 private OnNavigationSelectionChangedListener mOnNavigationSelectionChangedListener;
423 private OnNavigationRequestMenuListener mOnNavigationRequestMenuListener;
424 private OnFilePickedListener mOnFilePickedListener;
425 private OnDirectoryChangedListener mOnDirectoryChangedListener;
427 private boolean mChRooted;
429 private NAVIGATION_MODE mNavigationMode;
432 private Map<DisplayRestrictions, Object> mRestrictions;
434 private NavigationTask mNavigationTask;
439 Breadcrumb mBreadcrumb;
443 NavigationCustomTitleView mTitle;
447 AdapterView<?> mAdapterView;
449 //The layout for icons mode
450 private static final int RESOURCE_MODE_ICONS_LAYOUT = R.layout.navigation_view_icons;
451 private static final int RESOURCE_MODE_ICONS_ITEM = R.layout.navigation_view_icons_item;
452 //The layout for simple mode
453 private static final int RESOURCE_MODE_SIMPLE_LAYOUT = R.layout.navigation_view_simple;
454 private static final int RESOURCE_MODE_SIMPLE_ITEM = R.layout.navigation_view_simple_item;
455 //The layout for details mode
456 private static final int RESOURCE_MODE_DETAILS_LAYOUT = R.layout.navigation_view_details;
457 private static final int RESOURCE_MODE_DETAILS_ITEM = R.layout.navigation_view_details_item;
459 //The current layout identifier (is shared for all the mode layout)
460 private static final int RESOURCE_CURRENT_LAYOUT = R.id.navigation_view_layout;
463 * Constructor of <code>NavigationView</code>.
465 * @param context The current context
466 * @param attrs The attributes of the XML tag that is inflating the view.
468 public NavigationView(Context context, AttributeSet attrs) {
469 super(context, attrs);
470 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Navigable);
479 * Constructor of <code>NavigationView</code>.
481 * @param context The current context
482 * @param attrs The attributes of the XML tag that is inflating the view.
483 * @param defStyle The default style to apply to this view. If 0, no style
484 * will be applied (beyond what is included in the theme). This may
485 * either be an attribute resource, whose value will be retrieved
486 * from the current theme, or an explicit style resource.
488 public NavigationView(Context context, AttributeSet attrs, int defStyle) {
489 super(context, attrs, defStyle);
490 TypedArray a = context.obtainStyledAttributes(
491 attrs, R.styleable.Navigable, defStyle, 0);
500 * Invoked when the instance need to be saved.
502 * @return NavigationViewInfoParcelable The serialized info
504 public NavigationViewInfoParcelable onSaveState() {
505 //Return the persistent the data
506 NavigationViewInfoParcelable parcel = new NavigationViewInfoParcelable();
507 parcel.setId(this.mId);
508 parcel.setCurrentDir(this.mPreviousDir);
509 parcel.setChRooted(this.mChRooted);
510 parcel.setSelectedFiles(this.mAdapter.getSelectedItems());
511 parcel.setFiles(this.mFiles);
513 int firstVisiblePosition = mAdapterView.getFirstVisiblePosition();
514 if (firstVisiblePosition >= 0 && firstVisiblePosition < mAdapter.getCount()) {
515 FileSystemObject firstVisible = mAdapter
516 .getItem(firstVisiblePosition);
517 parcel.setFirstVisible(firstVisible);
524 * Invoked when the instance need to be restored.
526 * @param info The serialized info
527 * @return boolean If can restore
529 public boolean onRestoreState(NavigationViewInfoParcelable info) {
531 this.mId = info.getId();
532 this.mCurrentDir = info.getCurrentDir();
533 this.mChRooted = info.getChRooted();
534 this.mFiles = info.getFiles();
535 this.mAdapter.setSelectedItems(info.getSelectedFiles());
537 final FileSystemObject firstVisible = info.getFirstVisible();
540 refresh(firstVisible);
545 * Method that initializes the view. This method loads all the necessary
546 * information and create an appropriate layout for the view.
548 * @param tarray The type array
550 private void init(TypedArray tarray) {
552 this.mNavigationMode = NAVIGATION_MODE.BROWSABLE;
553 int mode = tarray.getInteger(
554 R.styleable.Navigable_navigation,
555 NAVIGATION_MODE.BROWSABLE.ordinal());
556 if (mode >= 0 && mode < NAVIGATION_MODE.values().length) {
557 this.mNavigationMode = NAVIGATION_MODE.values()[mode];
560 // Initialize default restrictions (no restrictions)
561 this.mRestrictions = new HashMap<DisplayRestrictions, Object>();
563 //Initialize variables
564 this.mFiles = new ArrayList<FileSystemObject>();
566 // Is ChRooted environment?
567 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
568 // Pick mode is always ChRooted
569 this.mChRooted = true;
572 FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
575 //Retrieve the default configuration
576 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
577 SharedPreferences preferences = Preferences.getSharedPreferences();
578 int viewMode = preferences.getInt(
579 FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
580 ((ObjectIdentifier)FileManagerSettings.
581 SETTINGS_LAYOUT_MODE.getDefaultValue()).getId());
582 changeViewMode(NavigationLayoutMode.fromId(viewMode));
584 // Pick mode has always a details layout
585 changeViewMode(NavigationLayoutMode.DETAILS);
590 * Method that returns the display restrictions to apply to this view.
592 * @return Map<DisplayRestrictions, Object> The restrictions to apply
594 public Map<DisplayRestrictions, Object> getRestrictions() {
595 return this.mRestrictions;
599 * Method that sets the display restrictions to apply to this view.
601 * @param mRestrictions The restrictions to apply
603 public void setRestrictions(Map<DisplayRestrictions, Object> mRestrictions) {
604 this.mRestrictions = mRestrictions;
608 * Method that returns the current file list of the navigation view.
610 * @return List<FileSystemObject> The current file list of the navigation view
612 public List<FileSystemObject> getFiles() {
613 if (this.mFiles == null) {
616 return new ArrayList<FileSystemObject>(this.mFiles);
620 * Method that returns the current file list of the navigation view.
622 * @return List<FileSystemObject> The current file list of the navigation view
624 public List<FileSystemObject> getSelectedFiles() {
625 if (this.mAdapter != null && this.mAdapter.getSelectedItems() != null) {
626 return new ArrayList<FileSystemObject>(this.mAdapter.getSelectedItems());
632 * Method that returns the custom title fragment associated with this navigation view.
634 * @return NavigationCustomTitleView The custom title view fragment
636 public NavigationCustomTitleView getCustomTitle() {
641 * Method that associates the custom title fragment with this navigation view.
643 * @param title The custom title view fragment
645 public void setCustomTitle(NavigationCustomTitleView title) {
650 * Method that returns the breadcrumb associated with this navigation view.
652 * @return Breadcrumb The breadcrumb view fragment
654 public Breadcrumb getBreadcrumb() {
655 return this.mBreadcrumb;
659 * Method that associates the breadcrumb with this navigation view.
661 * @param breadcrumb The breadcrumb view fragment
663 public void setBreadcrumb(Breadcrumb breadcrumb) {
664 this.mBreadcrumb = breadcrumb;
665 this.mBreadcrumb.addBreadcrumbListener(this);
669 * Method that sets the listener for communicate history changes.
671 * @param onHistoryListener The listener for communicate history changes
673 public void setOnHistoryListener(OnHistoryListener onHistoryListener) {
674 this.mOnHistoryListener = onHistoryListener;
678 * Method that sets the listener which communicates selection changes.
680 * @param onNavigationSelectionChangedListener The listener reference
682 public void setOnNavigationSelectionChangedListener(
683 OnNavigationSelectionChangedListener onNavigationSelectionChangedListener) {
684 this.mOnNavigationSelectionChangedListener = onNavigationSelectionChangedListener;
688 * Method that sets the listener for menu item requests.
690 * @param onNavigationRequestMenuListener The listener reference
692 public void setOnNavigationOnRequestMenuListener(
693 OnNavigationRequestMenuListener onNavigationRequestMenuListener) {
694 this.mOnNavigationRequestMenuListener = onNavigationRequestMenuListener;
698 * @return the mOnFilePickedListener
700 public OnFilePickedListener getOnFilePickedListener() {
701 return this.mOnFilePickedListener;
705 * Method that sets the listener for picked items
707 * @param onFilePickedListener The listener reference
709 public void setOnFilePickedListener(OnFilePickedListener onFilePickedListener) {
710 this.mOnFilePickedListener = onFilePickedListener;
714 * Method that sets the listener for directory changes
716 * @param onDirectoryChangedListener The listener reference
718 public void setOnDirectoryChangedListener(
719 OnDirectoryChangedListener onDirectoryChangedListener) {
720 this.mOnDirectoryChangedListener = onDirectoryChangedListener;
724 * Method that sets if the view should use flinger gesture detection.
726 * @param useFlinger If the view should use flinger gesture detection
728 public void setUseFlinger(boolean useFlinger) {
729 if (this.mCurrentMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
733 // Set the flinger listener (only when navigate)
734 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
735 if (this.mAdapterView instanceof FlingerListView) {
737 ((FlingerListView)this.mAdapterView).
738 setOnItemFlingerListener(this.mOnItemFlingerListener);
740 ((FlingerListView)this.mAdapterView).setOnItemFlingerListener(null);
747 * Method that forces the view to scroll to the file system object passed.
749 * @param fso The file system object
751 public void scrollTo(final FileSystemObject fso) {
753 this.mAdapterView.post(new Runnable() {
759 int position = mAdapter.getPosition(fso);
760 mAdapterView.setSelection(position);
762 // Make the scrollbar appear
764 mAdapterView.scrollBy(0, 1);
765 mAdapterView.scrollBy(0, -1);
768 } catch (Exception e) {
769 mAdapterView.setSelection(0);
772 mAdapterView.setSelection(0);
780 * Method that refresh the view data.
782 public void refresh() {
787 * Method that refresh the view data.
789 * @param restore Restore previous position
791 public void refresh(boolean restore) {
792 FileSystemObject fso = null;
793 // Try to restore the previous scroll position
796 if (this.mAdapterView != null && this.mAdapter != null) {
797 int position = this.mAdapterView.getFirstVisiblePosition();
798 fso = this.mAdapter.getItem(position);
800 } catch (Throwable _throw) {/**NON BLOCK**/}
806 * Method that refresh the view data.
808 * @param scrollTo Scroll to object
810 public void refresh(FileSystemObject scrollTo) {
811 //Check that current directory was set
812 if (this.mCurrentDir == null || this.mFiles == null) {
816 boolean addToHistory = false;
817 boolean reload = true;
818 boolean useCurrent = false;
819 SearchInfoParcelable searchInfo = null;
821 String newDir = this.mCurrentDir;
822 if (this.mNavigationTask != null) {
823 addToHistory = this.mNavigationTask.mAddToHistory;
824 reload = this.mNavigationTask.mReload;
825 useCurrent = this.mNavigationTask.mUseCurrent;
826 searchInfo = this.mNavigationTask.mSearchInfo;
827 this.mNavigationTask.cancel(true);
828 this.mNavigationTask = null;
829 this.mCurrentDir = this.mPreviousDir;
830 this.mPreviousDir = null;
834 changeCurrentDir(newDir, addToHistory, reload, useCurrent, searchInfo, scrollTo);
838 * Method that recycles this object
840 public void recycle() {
841 if (this.mAdapter != null) {
842 this.mAdapter.dispose();
847 * Method that change the view mode.
849 * @param newMode The new mode
851 @SuppressWarnings("unchecked")
852 public void changeViewMode(final NavigationLayoutMode newMode) {
853 //Check that it is really necessary change the mode
854 if (this.mCurrentMode != null && this.mCurrentMode.compareTo(newMode) == 0) {
858 // If we should set the listview to response to flinger gesture detection
860 Preferences.getSharedPreferences().getBoolean(
861 FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
862 ((Boolean)FileManagerSettings.
863 SETTINGS_USE_FLINGER.
864 getDefaultValue()).booleanValue());
866 //Creates the new layout
867 AdapterView<ListAdapter> newView = null;
868 int itemResourceId = -1;
869 if (newMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
870 newView = (AdapterView<ListAdapter>)inflate(
871 getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
872 itemResourceId = RESOURCE_MODE_ICONS_ITEM;
874 } else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
875 newView = (AdapterView<ListAdapter>)inflate(
876 getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
877 itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
879 // Set the flinger listener (only when navigate)
880 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
881 if (useFlinger && newView instanceof FlingerListView) {
882 ((FlingerListView)newView).
883 setOnItemFlingerListener(this.mOnItemFlingerListener);
887 } else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
888 newView = (AdapterView<ListAdapter>)inflate(
889 getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
890 itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
892 // Set the flinger listener (only when navigate)
893 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
894 if (useFlinger && newView instanceof FlingerListView) {
895 ((FlingerListView)newView).
896 setOnItemFlingerListener(this.mOnItemFlingerListener);
901 //Get the current adapter and its adapter list
902 List<FileSystemObject> files = new ArrayList<FileSystemObject>(this.mFiles);
903 final AdapterView<ListAdapter> current =
904 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
905 FileSystemObjectAdapter adapter =
906 new FileSystemObjectAdapter(
908 new ArrayList<FileSystemObject>(),
910 this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0);
911 adapter.setOnSelectionChangedListener(this);
913 //Remove current layout
914 if (current != null) {
915 if (current.getAdapter() != null) {
916 //Save selected items before dispose adapter
917 FileSystemObjectAdapter currentAdapter =
918 ((FileSystemObjectAdapter)current.getAdapter());
919 adapter.setSelectedItems(currentAdapter.getSelectedItems());
920 currentAdapter.dispose();
925 adapter.addAll(files);
928 this.mAdapter = adapter;
929 newView.setAdapter(this.mAdapter);
930 newView.setOnItemClickListener(NavigationView.this);
933 this.mAdapterView = newView;
935 this.mCurrentMode = newMode;
937 // Pick mode doesn't implements the onlongclick
938 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
939 this.mAdapterView.setOnItemLongClickListener(this);
941 this.mAdapterView.setOnItemLongClickListener(null);
944 //Save the preference (only in navigation browse mode)
945 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
947 Preferences.savePreference(
948 FileManagerSettings.SETTINGS_LAYOUT_MODE, newMode, true);
949 } catch (Exception ex) {
950 Log.e(TAG, "Save of view mode preference fails", ex); //$NON-NLS-1$
956 * Method that removes a {@link FileSystemObject} from the view
958 * @param fso The file system object
960 public void removeItem(FileSystemObject fso) {
961 // Delete also from internal list
963 int cc = this.mFiles.size()-1;
964 for (int i = cc; i >= 0; i--) {
965 FileSystemObject f = this.mFiles.get(i);
966 if (f != null && f.compareTo(fso) == 0) {
967 this.mFiles.remove(i);
972 this.mAdapter.remove(fso);
976 * Method that removes a file system object from his path from the view
978 * @param path The file system object path
980 public void removeItem(String path) {
981 FileSystemObject fso = this.mAdapter.getItem(path);
983 this.mAdapter.remove(fso);
988 * Method that returns the current directory.
990 * @return String The current directory
992 public String getCurrentDir() {
993 return this.mCurrentDir;
997 * Method that changes the current directory of the view.
999 * @param newDir The new directory location
1001 public void changeCurrentDir(final String newDir) {
1002 changeCurrentDir(newDir, true, false, false, null, null);
1006 * Method that changes the current directory of the view.
1008 * @param newDir The new directory location
1009 * @param addToHistory Add the directory to history
1011 public void changeCurrentDir(final String newDir, boolean addToHistory) {
1012 changeCurrentDir(newDir, addToHistory, false, false, null, null);
1016 * Method that changes the current directory of the view.
1018 * @param newDir The new directory location
1019 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1021 public void changeCurrentDir(final String newDir, SearchInfoParcelable searchInfo) {
1022 changeCurrentDir(newDir, true, false, false, searchInfo, null);
1026 * Method that changes the current directory of the view.
1028 * @param newDir The new directory location
1029 * @param addToHistory Add the directory to history
1030 * @param reload Force the reload of the data
1031 * @param useCurrent If this method must use the actual data (for back actions)
1032 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1033 * @param scrollTo If not null, then listview must scroll to this item
1035 private void changeCurrentDir(
1036 final String newDir, final boolean addToHistory,
1037 final boolean reload, final boolean useCurrent,
1038 final SearchInfoParcelable searchInfo, final FileSystemObject scrollTo) {
1039 this.mPreviousDir = this.mCurrentDir;
1040 this.mCurrentDir = newDir;
1041 mNavigationTask = new NavigationTask(useCurrent, addToHistory, reload,
1042 searchInfo, scrollTo, mRestrictions, mChRooted);
1043 mNavigationTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, newDir);
1047 * Remove all unmounted files in the current selection
1049 public void removeUnmountedSelection() {
1050 List<FileSystemObject> selection = mAdapter.getSelectedItems();
1051 int cc = selection.size() - 1;
1052 for (int i = cc; i >= 0; i--) {
1053 FileSystemObject item = selection.get(i);
1054 VirtualMountPointConsole vc =
1055 VirtualMountPointConsole.getVirtualConsoleForPath(item.getFullPath());
1056 if (vc != null && !vc.isMounted()) {
1057 selection.remove(i);
1060 mAdapter.setSelectedItems(selection);
1061 mAdapter.notifyDataSetChanged();
1063 // Do not call the selection listener. This method is supposed to be called by the
1069 * Method invoked when a execution ends.
1071 * @param files The files obtains from the list
1072 * @param addToHistory If add path to history
1073 * @param isNewHistory If is new history
1074 * @param hasChanged If current directory was changed
1075 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1076 * @param newDir The new directory
1077 * @param scrollTo If not null, then listview must scroll to this item
1080 void onPostExecuteTask(
1081 List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
1082 boolean hasChanged, SearchInfoParcelable searchInfo,
1083 String newDir, final FileSystemObject scrollTo) {
1085 //Check that there is not errors and have some data
1086 if (files == null) {
1087 this.mCurrentDir = this.mPreviousDir;
1091 //Remove parent directory if we are in the root of a chrooted environment
1092 if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
1093 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
1099 if (addToHistory && hasChanged && isNewHistory) {
1100 if (this.mOnHistoryListener != null) {
1101 //Communicate the need of a history change
1102 this.mOnHistoryListener.onNewHistory(onSaveState());
1108 this.mFiles = files;
1109 if (searchInfo != null) {
1110 searchInfo.setSuccessNavigation(true);
1113 //Change the breadcrumb
1114 if (this.mBreadcrumb != null) {
1115 this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
1118 //If scrollTo is null, the position will be set to 0
1121 //The current directory is now the "newDir"
1122 if (this.mOnDirectoryChangedListener != null) {
1123 FileSystemObject dir = FileHelper.createFileSystemObject(new File(newDir));
1124 this.mOnDirectoryChangedListener.onDirectoryChanged(dir);
1128 //If calling activity is search, then save the search history
1129 if (searchInfo != null) {
1130 this.mOnHistoryListener.onNewHistory(searchInfo);
1133 this.mPreviousDir = null;
1134 mNavigationTask = null;
1136 //End of loading data
1138 NavigationView.this.mBreadcrumb.endLoading();
1139 } catch (Throwable ex) {
1146 * Method that loads the files in the adapter.
1148 * @param files The files to load in the adapter
1151 @SuppressWarnings("unchecked")
1152 private void loadData(final List<FileSystemObject> files) {
1153 //Notify data to adapter view
1154 final AdapterView<ListAdapter> view =
1155 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
1156 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
1157 adapter.setNotifyOnChange(false);
1159 adapter.addAll(files);
1160 adapter.notifyDataSetChanged();
1167 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1168 // Different actions depending on user preference
1170 // Get the adapter and the fso
1171 FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
1172 if (adapter == null || position < 0 || (position >= adapter.getCount())) {
1175 FileSystemObject fso = adapter.getItem(position);
1177 // Parent directory hasn't actions
1178 if (fso instanceof ParentDirectory) {
1182 // Pick mode doesn't implements the onlongclick
1183 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
1188 return true; //Always consume the event
1192 * Method that opens or navigates to the {@link FileSystemObject}
1194 * @param fso The file system object
1196 public void open(FileSystemObject fso) {
1201 * Method that opens or navigates to the {@link FileSystemObject}
1203 * @param fso The file system object
1204 * @param searchInfo The search info
1206 public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
1207 // If is a folder, then navigate to
1208 if (FileHelper.isDirectory(fso)) {
1209 changeCurrentDir(fso.getFullPath(), searchInfo);
1211 // Open the file with the preferred registered app
1212 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1220 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1222 FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
1223 if (fso instanceof ParentDirectory) {
1224 changeCurrentDir(fso.getParent(), true, false, false, null, null);
1226 } else if (fso instanceof Directory) {
1227 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
1229 } else if (fso instanceof Symlink) {
1230 Symlink symlink = (Symlink)fso;
1231 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
1233 symlink.getLinkRef().getFullPath(), true, false, false, null, null);
1237 // Open the link ref
1238 fso = symlink.getLinkRef();
1241 // Open the file (edit or pick)
1242 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
1243 // Open the file with the preferred registered app
1244 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1246 // Request a file pick selection
1247 if (this.mOnFilePickedListener != null) {
1248 this.mOnFilePickedListener.onFilePicked(fso);
1251 } catch (Throwable ex) {
1252 ExceptionUtil.translateException(getContext(), ex);
1260 public void onRequestRefresh(Object o, boolean clearSelection) {
1261 if (o instanceof FileSystemObject) {
1262 refresh((FileSystemObject)o);
1263 } else if (o == null) {
1266 if (clearSelection) {
1275 public void onRequestBookmarksRefresh() {
1283 public void onRequestRemove(Object o, boolean clearSelection) {
1284 if (o != null && o instanceof FileSystemObject) {
1285 removeItem((FileSystemObject)o);
1287 onRequestRefresh(null, clearSelection);
1289 if (clearSelection) {
1298 public void onNavigateTo(Object o) {
1303 public void onCancel() {
1311 public void onBreadcrumbItemClick(BreadcrumbItem item) {
1312 changeCurrentDir(item.getItemPath(), true, true, false, null, null);
1319 public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
1320 if (this.mOnNavigationSelectionChangedListener != null) {
1321 this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
1326 * Method invoked when a request to show the menu associated
1327 * with an item is started.
1329 * @param item The item for which the request was started
1331 public void onRequestMenu(final FileSystemObject item) {
1332 if (this.mOnNavigationRequestMenuListener != null) {
1333 this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
1341 public void onToggleSelection(FileSystemObject fso) {
1342 if (this.mAdapter != null) {
1343 this.mAdapter.toggleSelection(fso);
1351 public void onDeselectAll() {
1352 if (this.mAdapter != null) {
1353 this.mAdapter.deselectedAll();
1361 public void onSelectAllVisibleItems() {
1362 if (this.mAdapter != null) {
1363 this.mAdapter.selectedAllVisibleItems();
1371 public void onDeselectAllVisibleItems() {
1372 if (this.mAdapter != null) {
1373 this.mAdapter.deselectedAllVisibleItems();
1381 public List<FileSystemObject> onRequestSelectedFiles() {
1382 return this.getSelectedFiles();
1389 public List<FileSystemObject> onRequestCurrentItems() {
1390 return this.getFiles();
1397 public String onRequestCurrentDir() {
1398 return this.mCurrentDir;
1402 * Method that creates a ChRooted environment, protecting the user to break anything
1406 public void createChRooted() {
1407 // If we are in a ChRooted environment, then do nothing
1408 if (this.mChRooted) return;
1409 this.mChRooted = true;
1411 //Change to first storage volume
1412 StorageVolume[] volumes =
1413 StorageHelper.getStorageVolumes(getContext(), false);
1414 if (volumes != null && volumes.length > 0) {
1415 changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1420 * Method that exits from a ChRooted environment
1423 public void exitChRooted() {
1424 // If we aren't in a ChRooted environment, then do nothing
1425 if (!this.mChRooted) return;
1426 this.mChRooted = false;
1433 * Method that ensures that the user don't go outside the ChRooted environment
1435 * @param newDir The new directory to navigate to
1438 private String checkChRootedNavigation(String newDir) {
1439 // If we aren't in ChRooted environment, then there is nothing to check
1440 if (!this.mChRooted) return newDir;
1442 // Check if the path is owned by one of the storage volumes
1443 if (!StorageHelper.isPathInStorageVolume(newDir)) {
1444 StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext(), false);
1445 if (volumes != null && volumes.length > 0) {
1446 return volumes[0].getPath();
1453 * Method that applies the current theme to the activity
1455 public void applyTheme() {
1457 if (getBreadcrumb() != null) {
1458 getBreadcrumb().applyTheme();
1461 //- Redraw the adapter view
1462 Theme theme = ThemeManager.getCurrentTheme(getContext());
1463 theme.setBackgroundDrawable(getContext(), this, "background_drawable"); //$NON-NLS-1$
1464 if (this.mAdapter != null) {
1465 this.mAdapter.notifyThemeChanged();
1467 if (this.mAdapterView instanceof ListView) {
1468 ((ListView)this.mAdapterView).setDivider(
1469 theme.getDrawable(getContext(), "horizontal_divider_drawable")); //$NON-NLS-1$