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;
224 public NavigationTask(boolean useCurrent, boolean addToHistory, boolean reload,
225 SearchInfoParcelable searchInfo, FileSystemObject scrollTo) {
227 this.mUseCurrent = useCurrent;
228 this.mAddToHistory = addToHistory;
229 this.mSearchInfo = searchInfo;
230 this.mReload = reload;
231 this.mScrollTo = scrollTo;
238 protected List<FileSystemObject> doInBackground(String... params) {
239 // Check navigation security (don't allow to go outside the ChRooted environment if one
241 mNewDirChecked = checkChRootedNavigation(params[0]);
243 //Check that it is really necessary change the directory
244 if (!mReload && NavigationView.this.mCurrentDir != null &&
245 NavigationView.this.mCurrentDir.compareTo(mNewDirChecked) == 0) {
249 mHasChanged = !(NavigationView.this.mCurrentDir != null &&
250 NavigationView.this.mCurrentDir.compareTo(mNewDirChecked) == 0);
251 mIsNewHistory = (NavigationView.this.mCurrentDir != null);
254 //Reset the custom title view and returns to breadcrumb
255 if (NavigationView.this.mTitle != null) {
256 NavigationView.this.mTitle.post(new Runnable() {
260 NavigationView.this.mTitle.restoreView();
261 } catch (Exception e) {
269 //Start of loading data
270 if (NavigationView.this.mBreadcrumb != null) {
272 NavigationView.this.mBreadcrumb.startLoading();
273 } catch (Throwable ex) {
278 //Get the files, resolve links and apply configuration
279 //(sort, hidden, ...)
280 List<FileSystemObject> files = NavigationView.this.mFiles;
282 files = CommandHelper.listFiles(getContext(), mNewDirChecked, null);
286 } catch (final ConsoleAllocException e) {
287 //Show exception and exists
288 NavigationView.this.post(new Runnable() {
291 Context ctx = getContext();
292 Log.e(TAG, ctx.getString(
293 R.string.msgs_cant_create_console), e);
294 DialogHelper.showToast(ctx,
295 R.string.msgs_cant_create_console,
297 ((Activity)ctx).finish();
301 } catch (Exception ex) {
302 //End of loading data
303 if (NavigationView.this.mBreadcrumb != null) {
305 NavigationView.this.mBreadcrumb.endLoading();
306 } catch (Throwable ex2) {
310 if (ex instanceof CancelledOperationException) {
314 //Capture exception (attach task, and use listener to do the anim)
315 ExceptionUtil.attachAsyncTask(
317 new AsyncTask<Object, Integer, Boolean>() {
318 private List<FileSystemObject> mTaskFiles = null;
321 "unchecked", "unqualified-field-access"
323 protected Boolean doInBackground(Object... taskParams) {
324 mTaskFiles = (List<FileSystemObject>)taskParams[0];
329 @SuppressWarnings("unqualified-field-access")
330 protected void onPostExecute(Boolean result) {
331 if (!result.booleanValue()) {
335 mTaskFiles, mAddToHistory, mIsNewHistory, mHasChanged,
336 mSearchInfo, mNewDirChecked, mScrollTo);
339 final OnRelaunchCommandResult exListener =
340 new OnRelaunchCommandResult() {
342 public void onSuccess() {
346 public void onFailed(Throwable cause) {
350 public void onCancelled() {
353 private void done() {
358 ExceptionUtil.translateException(
359 getContext(), ex, false, true, exListener);
368 protected void onCancelled(List<FileSystemObject> result) {
376 protected void onPostExecute(List<FileSystemObject> files) {
377 // This means an exception. This method will be recalled then
379 onPostExecuteTask(files, mAddToHistory, mIsNewHistory, mHasChanged,
380 mSearchInfo, mNewDirChecked, mScrollTo);
388 * Method that performs a fade animation.
390 * @param out Fade out (true); Fade in (false)
392 void fadeEfect(final boolean out) {
393 Activity activity = (Activity)getContext();
394 activity.runOnUiThread(new Runnable() {
397 Animation fadeAnim = out ?
398 new AlphaAnimation(1, 0) :
399 new AlphaAnimation(0, 1);
400 fadeAnim.setDuration(50L);
401 fadeAnim.setFillAfter(true);
402 fadeAnim.setInterpolator(new AccelerateInterpolator());
403 NavigationView.this.startAnimation(fadeAnim);
410 private String mCurrentDir;
411 private NavigationLayoutMode mCurrentMode;
415 List<FileSystemObject> mFiles;
416 private FileSystemObjectAdapter mAdapter;
418 private OnHistoryListener mOnHistoryListener;
419 private OnNavigationSelectionChangedListener mOnNavigationSelectionChangedListener;
420 private OnNavigationRequestMenuListener mOnNavigationRequestMenuListener;
421 private OnFilePickedListener mOnFilePickedListener;
422 private OnDirectoryChangedListener mOnDirectoryChangedListener;
424 private boolean mChRooted;
426 private NAVIGATION_MODE mNavigationMode;
429 private Map<DisplayRestrictions, Object> mRestrictions;
434 Breadcrumb mBreadcrumb;
438 NavigationCustomTitleView mTitle;
442 AdapterView<?> mAdapterView;
444 //The layout for icons mode
445 private static final int RESOURCE_MODE_ICONS_LAYOUT = R.layout.navigation_view_icons;
446 private static final int RESOURCE_MODE_ICONS_ITEM = R.layout.navigation_view_icons_item;
447 //The layout for simple mode
448 private static final int RESOURCE_MODE_SIMPLE_LAYOUT = R.layout.navigation_view_simple;
449 private static final int RESOURCE_MODE_SIMPLE_ITEM = R.layout.navigation_view_simple_item;
450 //The layout for details mode
451 private static final int RESOURCE_MODE_DETAILS_LAYOUT = R.layout.navigation_view_details;
452 private static final int RESOURCE_MODE_DETAILS_ITEM = R.layout.navigation_view_details_item;
454 //The current layout identifier (is shared for all the mode layout)
455 private static final int RESOURCE_CURRENT_LAYOUT = R.id.navigation_view_layout;
458 * Constructor of <code>NavigationView</code>.
460 * @param context The current context
461 * @param attrs The attributes of the XML tag that is inflating the view.
463 public NavigationView(Context context, AttributeSet attrs) {
464 super(context, attrs);
465 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Navigable);
474 * Constructor of <code>NavigationView</code>.
476 * @param context The current context
477 * @param attrs The attributes of the XML tag that is inflating the view.
478 * @param defStyle The default style to apply to this view. If 0, no style
479 * will be applied (beyond what is included in the theme). This may
480 * either be an attribute resource, whose value will be retrieved
481 * from the current theme, or an explicit style resource.
483 public NavigationView(Context context, AttributeSet attrs, int defStyle) {
484 super(context, attrs, defStyle);
485 TypedArray a = context.obtainStyledAttributes(
486 attrs, R.styleable.Navigable, defStyle, 0);
495 * Invoked when the instance need to be saved.
497 * @return NavigationViewInfoParcelable The serialized info
499 public NavigationViewInfoParcelable onSaveState() {
500 //Return the persistent the data
501 NavigationViewInfoParcelable parcel = new NavigationViewInfoParcelable();
502 parcel.setId(this.mId);
503 parcel.setCurrentDir(this.mCurrentDir);
504 parcel.setChRooted(this.mChRooted);
505 parcel.setSelectedFiles(this.mAdapter.getSelectedItems());
506 parcel.setFiles(this.mFiles);
508 int firstVisiblePosition = mAdapterView.getFirstVisiblePosition();
509 if (firstVisiblePosition >= 0 && firstVisiblePosition < mAdapter.getCount()) {
510 FileSystemObject firstVisible = mAdapter
511 .getItem(firstVisiblePosition);
512 parcel.setFirstVisible(firstVisible);
519 * Invoked when the instance need to be restored.
521 * @param info The serialized info
522 * @return boolean If can restore
524 public boolean onRestoreState(NavigationViewInfoParcelable info) {
526 this.mId = info.getId();
527 this.mCurrentDir = info.getCurrentDir();
528 this.mChRooted = info.getChRooted();
529 this.mFiles = info.getFiles();
530 this.mAdapter.setSelectedItems(info.getSelectedFiles());
532 final FileSystemObject firstVisible = info.getFirstVisible();
535 refresh(firstVisible);
540 * Method that initializes the view. This method loads all the necessary
541 * information and create an appropriate layout for the view.
543 * @param tarray The type array
545 private void init(TypedArray tarray) {
547 this.mNavigationMode = NAVIGATION_MODE.BROWSABLE;
548 int mode = tarray.getInteger(
549 R.styleable.Navigable_navigation,
550 NAVIGATION_MODE.BROWSABLE.ordinal());
551 if (mode >= 0 && mode < NAVIGATION_MODE.values().length) {
552 this.mNavigationMode = NAVIGATION_MODE.values()[mode];
555 // Initialize default restrictions (no restrictions)
556 this.mRestrictions = new HashMap<DisplayRestrictions, Object>();
558 //Initialize variables
559 this.mFiles = new ArrayList<FileSystemObject>();
561 // Is ChRooted environment?
562 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
563 // Pick mode is always ChRooted
564 this.mChRooted = true;
567 FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
570 //Retrieve the default configuration
571 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
572 SharedPreferences preferences = Preferences.getSharedPreferences();
573 int viewMode = preferences.getInt(
574 FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
575 ((ObjectIdentifier)FileManagerSettings.
576 SETTINGS_LAYOUT_MODE.getDefaultValue()).getId());
577 changeViewMode(NavigationLayoutMode.fromId(viewMode));
579 // Pick mode has always a details layout
580 changeViewMode(NavigationLayoutMode.DETAILS);
585 * Method that returns the display restrictions to apply to this view.
587 * @return Map<DisplayRestrictions, Object> The restrictions to apply
589 public Map<DisplayRestrictions, Object> getRestrictions() {
590 return this.mRestrictions;
594 * Method that sets the display restrictions to apply to this view.
596 * @param mRestrictions The restrictions to apply
598 public void setRestrictions(Map<DisplayRestrictions, Object> mRestrictions) {
599 this.mRestrictions = mRestrictions;
603 * Method that returns the current file list of the navigation view.
605 * @return List<FileSystemObject> The current file list of the navigation view
607 public List<FileSystemObject> getFiles() {
608 if (this.mFiles == null) {
611 return new ArrayList<FileSystemObject>(this.mFiles);
615 * Method that returns the current file list of the navigation view.
617 * @return List<FileSystemObject> The current file list of the navigation view
619 public List<FileSystemObject> getSelectedFiles() {
620 if (this.mAdapter != null && this.mAdapter.getSelectedItems() != null) {
621 return new ArrayList<FileSystemObject>(this.mAdapter.getSelectedItems());
627 * Method that returns the custom title fragment associated with this navigation view.
629 * @return NavigationCustomTitleView The custom title view fragment
631 public NavigationCustomTitleView getCustomTitle() {
636 * Method that associates the custom title fragment with this navigation view.
638 * @param title The custom title view fragment
640 public void setCustomTitle(NavigationCustomTitleView title) {
645 * Method that returns the breadcrumb associated with this navigation view.
647 * @return Breadcrumb The breadcrumb view fragment
649 public Breadcrumb getBreadcrumb() {
650 return this.mBreadcrumb;
654 * Method that associates the breadcrumb with this navigation view.
656 * @param breadcrumb The breadcrumb view fragment
658 public void setBreadcrumb(Breadcrumb breadcrumb) {
659 this.mBreadcrumb = breadcrumb;
660 this.mBreadcrumb.addBreadcrumbListener(this);
664 * Method that sets the listener for communicate history changes.
666 * @param onHistoryListener The listener for communicate history changes
668 public void setOnHistoryListener(OnHistoryListener onHistoryListener) {
669 this.mOnHistoryListener = onHistoryListener;
673 * Method that sets the listener which communicates selection changes.
675 * @param onNavigationSelectionChangedListener The listener reference
677 public void setOnNavigationSelectionChangedListener(
678 OnNavigationSelectionChangedListener onNavigationSelectionChangedListener) {
679 this.mOnNavigationSelectionChangedListener = onNavigationSelectionChangedListener;
683 * Method that sets the listener for menu item requests.
685 * @param onNavigationRequestMenuListener The listener reference
687 public void setOnNavigationOnRequestMenuListener(
688 OnNavigationRequestMenuListener onNavigationRequestMenuListener) {
689 this.mOnNavigationRequestMenuListener = onNavigationRequestMenuListener;
693 * @return the mOnFilePickedListener
695 public OnFilePickedListener getOnFilePickedListener() {
696 return this.mOnFilePickedListener;
700 * Method that sets the listener for picked items
702 * @param onFilePickedListener The listener reference
704 public void setOnFilePickedListener(OnFilePickedListener onFilePickedListener) {
705 this.mOnFilePickedListener = onFilePickedListener;
709 * Method that sets the listener for directory changes
711 * @param onDirectoryChangedListener The listener reference
713 public void setOnDirectoryChangedListener(
714 OnDirectoryChangedListener onDirectoryChangedListener) {
715 this.mOnDirectoryChangedListener = onDirectoryChangedListener;
719 * Method that sets if the view should use flinger gesture detection.
721 * @param useFlinger If the view should use flinger gesture detection
723 public void setUseFlinger(boolean useFlinger) {
724 if (this.mCurrentMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
728 // Set the flinger listener (only when navigate)
729 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
730 if (this.mAdapterView instanceof FlingerListView) {
732 ((FlingerListView)this.mAdapterView).
733 setOnItemFlingerListener(this.mOnItemFlingerListener);
735 ((FlingerListView)this.mAdapterView).setOnItemFlingerListener(null);
742 * Method that forces the view to scroll to the file system object passed.
744 * @param fso The file system object
746 public void scrollTo(final FileSystemObject fso) {
748 this.mAdapterView.post(new Runnable() {
754 int position = mAdapter.getPosition(fso);
755 mAdapterView.setSelection(position);
757 // Make the scrollbar appear
759 mAdapterView.scrollBy(0, 1);
760 mAdapterView.scrollBy(0, -1);
763 } catch (Exception e) {
764 mAdapterView.setSelection(0);
767 mAdapterView.setSelection(0);
775 * Method that refresh the view data.
777 public void refresh() {
782 * Method that refresh the view data.
784 * @param restore Restore previous position
786 public void refresh(boolean restore) {
787 FileSystemObject fso = null;
788 // Try to restore the previous scroll position
791 if (this.mAdapterView != null && this.mAdapter != null) {
792 int position = this.mAdapterView.getFirstVisiblePosition();
793 fso = this.mAdapter.getItem(position);
795 } catch (Throwable _throw) {/**NON BLOCK**/}
801 * Method that refresh the view data.
803 * @param scrollTo Scroll to object
805 public void refresh(FileSystemObject scrollTo) {
806 //Check that current directory was set
807 if (this.mCurrentDir == null || this.mFiles == null) {
812 changeCurrentDir(this.mCurrentDir, false, true, false, null, scrollTo);
816 * Method that recycles this object
818 public void recycle() {
819 if (this.mAdapter != null) {
820 this.mAdapter.dispose();
825 * Method that change the view mode.
827 * @param newMode The new mode
829 @SuppressWarnings("unchecked")
830 public void changeViewMode(final NavigationLayoutMode newMode) {
831 //Check that it is really necessary change the mode
832 if (this.mCurrentMode != null && this.mCurrentMode.compareTo(newMode) == 0) {
836 // If we should set the listview to response to flinger gesture detection
838 Preferences.getSharedPreferences().getBoolean(
839 FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
840 ((Boolean)FileManagerSettings.
841 SETTINGS_USE_FLINGER.
842 getDefaultValue()).booleanValue());
844 //Creates the new layout
845 AdapterView<ListAdapter> newView = null;
846 int itemResourceId = -1;
847 if (newMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
848 newView = (AdapterView<ListAdapter>)inflate(
849 getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
850 itemResourceId = RESOURCE_MODE_ICONS_ITEM;
852 } else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
853 newView = (AdapterView<ListAdapter>)inflate(
854 getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
855 itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
857 // Set the flinger listener (only when navigate)
858 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
859 if (useFlinger && newView instanceof FlingerListView) {
860 ((FlingerListView)newView).
861 setOnItemFlingerListener(this.mOnItemFlingerListener);
865 } else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
866 newView = (AdapterView<ListAdapter>)inflate(
867 getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
868 itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
870 // Set the flinger listener (only when navigate)
871 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
872 if (useFlinger && newView instanceof FlingerListView) {
873 ((FlingerListView)newView).
874 setOnItemFlingerListener(this.mOnItemFlingerListener);
879 //Get the current adapter and its adapter list
880 List<FileSystemObject> files = new ArrayList<FileSystemObject>(this.mFiles);
881 final AdapterView<ListAdapter> current =
882 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
883 FileSystemObjectAdapter adapter =
884 new FileSystemObjectAdapter(
886 new ArrayList<FileSystemObject>(),
888 this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0);
889 adapter.setOnSelectionChangedListener(this);
891 //Remove current layout
892 if (current != null) {
893 if (current.getAdapter() != null) {
894 //Save selected items before dispose adapter
895 FileSystemObjectAdapter currentAdapter =
896 ((FileSystemObjectAdapter)current.getAdapter());
897 adapter.setSelectedItems(currentAdapter.getSelectedItems());
898 currentAdapter.dispose();
903 adapter.addAll(files);
906 this.mAdapter = adapter;
907 newView.setAdapter(this.mAdapter);
908 newView.setOnItemClickListener(NavigationView.this);
911 this.mAdapterView = newView;
913 this.mCurrentMode = newMode;
915 // Pick mode doesn't implements the onlongclick
916 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
917 this.mAdapterView.setOnItemLongClickListener(this);
919 this.mAdapterView.setOnItemLongClickListener(null);
922 //Save the preference (only in navigation browse mode)
923 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
925 Preferences.savePreference(
926 FileManagerSettings.SETTINGS_LAYOUT_MODE, newMode, true);
927 } catch (Exception ex) {
928 Log.e(TAG, "Save of view mode preference fails", ex); //$NON-NLS-1$
934 * Method that removes a {@link FileSystemObject} from the view
936 * @param fso The file system object
938 public void removeItem(FileSystemObject fso) {
939 // Delete also from internal list
941 int cc = this.mFiles.size()-1;
942 for (int i = cc; i >= 0; i--) {
943 FileSystemObject f = this.mFiles.get(i);
944 if (f != null && f.compareTo(fso) == 0) {
945 this.mFiles.remove(i);
950 this.mAdapter.remove(fso);
954 * Method that removes a file system object from his path from the view
956 * @param path The file system object path
958 public void removeItem(String path) {
959 FileSystemObject fso = this.mAdapter.getItem(path);
961 this.mAdapter.remove(fso);
966 * Method that returns the current directory.
968 * @return String The current directory
970 public String getCurrentDir() {
971 return this.mCurrentDir;
975 * Method that changes the current directory of the view.
977 * @param newDir The new directory location
979 public void changeCurrentDir(final String newDir) {
980 changeCurrentDir(newDir, true, false, false, null, null);
984 * Method that changes the current directory of the view.
986 * @param newDir The new directory location
987 * @param addToHistory Add the directory to history
989 public void changeCurrentDir(final String newDir, boolean addToHistory) {
990 changeCurrentDir(newDir, addToHistory, false, false, null, null);
994 * Method that changes the current directory of the view.
996 * @param newDir The new directory location
997 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
999 public void changeCurrentDir(final String newDir, SearchInfoParcelable searchInfo) {
1000 changeCurrentDir(newDir, true, false, false, searchInfo, null);
1004 * Method that changes the current directory of the view.
1006 * @param newDir The new directory location
1007 * @param addToHistory Add the directory to history
1008 * @param reload Force the reload of the data
1009 * @param useCurrent If this method must use the actual data (for back actions)
1010 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1011 * @param scrollTo If not null, then listview must scroll to this item
1013 private void changeCurrentDir(
1014 final String newDir, final boolean addToHistory,
1015 final boolean reload, final boolean useCurrent,
1016 final SearchInfoParcelable searchInfo, final FileSystemObject scrollTo) {
1017 NavigationTask task = new NavigationTask(useCurrent, addToHistory, reload,
1018 searchInfo, scrollTo);
1019 task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, newDir);
1023 * Remove all unmounted files in the current selection
1025 public void removeUnmountedSelection() {
1026 List<FileSystemObject> selection = mAdapter.getSelectedItems();
1027 int cc = selection.size() - 1;
1028 for (int i = cc; i >= 0; i--) {
1029 FileSystemObject item = selection.get(i);
1030 VirtualMountPointConsole vc =
1031 VirtualMountPointConsole.getVirtualConsoleForPath(item.getFullPath());
1032 if (vc != null && !vc.isMounted()) {
1033 selection.remove(i);
1036 mAdapter.setSelectedItems(selection);
1037 mAdapter.notifyDataSetChanged();
1039 // Do not call the selection listener. This method is supposed to be called by the
1045 * Method invoked when a execution ends.
1047 * @param files The files obtains from the list
1048 * @param addToHistory If add path to history
1049 * @param isNewHistory If is new history
1050 * @param hasChanged If current directory was changed
1051 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1052 * @param newDir The new directory
1053 * @param scrollTo If not null, then listview must scroll to this item
1056 void onPostExecuteTask(
1057 List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
1058 boolean hasChanged, SearchInfoParcelable searchInfo,
1059 String newDir, final FileSystemObject scrollTo) {
1061 //Check that there is not errors and have some data
1062 if (files == null) {
1066 //Apply user preferences
1067 List<FileSystemObject> sortedFiles =
1068 FileHelper.applyUserPreferences(files, this.mRestrictions, this.mChRooted);
1070 //Remove parent directory if we are in the root of a chrooted environment
1071 if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
1072 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
1078 if (addToHistory && hasChanged && isNewHistory) {
1079 if (this.mOnHistoryListener != null) {
1080 //Communicate the need of a history change
1081 this.mOnHistoryListener.onNewHistory(onSaveState());
1086 loadData(sortedFiles);
1087 this.mFiles = sortedFiles;
1088 if (searchInfo != null) {
1089 searchInfo.setSuccessNavigation(true);
1092 //Change the breadcrumb
1093 if (this.mBreadcrumb != null) {
1094 this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
1097 //If scrollTo is null, the position will be set to 0
1100 //The current directory is now the "newDir"
1101 this.mCurrentDir = newDir;
1102 if (this.mOnDirectoryChangedListener != null) {
1103 FileSystemObject dir = FileHelper.createFileSystemObject(new File(newDir));
1104 this.mOnDirectoryChangedListener.onDirectoryChanged(dir);
1107 //If calling activity is search, then save the search history
1108 if (searchInfo != null) {
1109 this.mOnHistoryListener.onNewHistory(searchInfo);
1112 //End of loading data
1114 NavigationView.this.mBreadcrumb.endLoading();
1115 } catch (Throwable ex) {
1122 * Method that loads the files in the adapter.
1124 * @param files The files to load in the adapter
1127 @SuppressWarnings("unchecked")
1128 private void loadData(final List<FileSystemObject> files) {
1129 //Notify data to adapter view
1130 final AdapterView<ListAdapter> view =
1131 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
1132 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
1133 adapter.setNotifyOnChange(false);
1135 adapter.addAll(files);
1136 adapter.notifyDataSetChanged();
1143 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1144 // Different actions depending on user preference
1146 // Get the adapter and the fso
1147 FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
1148 FileSystemObject fso = adapter.getItem(position);
1150 // Parent directory hasn't actions
1151 if (fso instanceof ParentDirectory) {
1155 // Pick mode doesn't implements the onlongclick
1156 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
1161 return true; //Always consume the event
1165 * Method that opens or navigates to the {@link FileSystemObject}
1167 * @param fso The file system object
1169 public void open(FileSystemObject fso) {
1174 * Method that opens or navigates to the {@link FileSystemObject}
1176 * @param fso The file system object
1177 * @param searchInfo The search info
1179 public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
1180 // If is a folder, then navigate to
1181 if (FileHelper.isDirectory(fso)) {
1182 changeCurrentDir(fso.getFullPath(), searchInfo);
1184 // Open the file with the preferred registered app
1185 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1193 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1195 FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
1196 if (fso instanceof ParentDirectory) {
1197 changeCurrentDir(fso.getParent(), true, false, false, null, null);
1199 } else if (fso instanceof Directory) {
1200 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
1202 } else if (fso instanceof Symlink) {
1203 Symlink symlink = (Symlink)fso;
1204 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
1206 symlink.getLinkRef().getFullPath(), true, false, false, null, null);
1210 // Open the link ref
1211 fso = symlink.getLinkRef();
1214 // Open the file (edit or pick)
1215 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
1216 // Open the file with the preferred registered app
1217 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1219 // Request a file pick selection
1220 if (this.mOnFilePickedListener != null) {
1221 this.mOnFilePickedListener.onFilePicked(fso);
1224 } catch (Throwable ex) {
1225 ExceptionUtil.translateException(getContext(), ex);
1233 public void onRequestRefresh(Object o, boolean clearSelection) {
1234 if (o instanceof FileSystemObject) {
1235 refresh((FileSystemObject)o);
1236 } else if (o == null) {
1239 if (clearSelection) {
1248 public void onRequestBookmarksRefresh() {
1256 public void onRequestRemove(Object o, boolean clearSelection) {
1257 if (o != null && o instanceof FileSystemObject) {
1258 removeItem((FileSystemObject)o);
1260 onRequestRefresh(null, clearSelection);
1262 if (clearSelection) {
1271 public void onNavigateTo(Object o) {
1276 public void onCancel() {
1284 public void onBreadcrumbItemClick(BreadcrumbItem item) {
1285 changeCurrentDir(item.getItemPath(), true, true, false, null, null);
1292 public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
1293 if (this.mOnNavigationSelectionChangedListener != null) {
1294 this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
1299 * Method invoked when a request to show the menu associated
1300 * with an item is started.
1302 * @param item The item for which the request was started
1304 public void onRequestMenu(final FileSystemObject item) {
1305 if (this.mOnNavigationRequestMenuListener != null) {
1306 this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
1314 public void onToggleSelection(FileSystemObject fso) {
1315 if (this.mAdapter != null) {
1316 this.mAdapter.toggleSelection(fso);
1324 public void onDeselectAll() {
1325 if (this.mAdapter != null) {
1326 this.mAdapter.deselectedAll();
1334 public void onSelectAllVisibleItems() {
1335 if (this.mAdapter != null) {
1336 this.mAdapter.selectedAllVisibleItems();
1344 public void onDeselectAllVisibleItems() {
1345 if (this.mAdapter != null) {
1346 this.mAdapter.deselectedAllVisibleItems();
1354 public List<FileSystemObject> onRequestSelectedFiles() {
1355 return this.getSelectedFiles();
1362 public List<FileSystemObject> onRequestCurrentItems() {
1363 return this.getFiles();
1370 public String onRequestCurrentDir() {
1371 return this.mCurrentDir;
1375 * Method that creates a ChRooted environment, protecting the user to break anything
1379 public void createChRooted() {
1380 // If we are in a ChRooted environment, then do nothing
1381 if (this.mChRooted) return;
1382 this.mChRooted = true;
1384 //Change to first storage volume
1385 StorageVolume[] volumes =
1386 StorageHelper.getStorageVolumes(getContext(), false);
1387 if (volumes != null && volumes.length > 0) {
1388 changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1393 * Method that exits from a ChRooted environment
1396 public void exitChRooted() {
1397 // If we aren't in a ChRooted environment, then do nothing
1398 if (!this.mChRooted) return;
1399 this.mChRooted = false;
1406 * Method that ensures that the user don't go outside the ChRooted environment
1408 * @param newDir The new directory to navigate to
1411 private String checkChRootedNavigation(String newDir) {
1412 // If we aren't in ChRooted environment, then there is nothing to check
1413 if (!this.mChRooted) return newDir;
1415 // Check if the path is owned by one of the storage volumes
1416 if (!StorageHelper.isPathInStorageVolume(newDir)) {
1417 StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext(), false);
1418 if (volumes != null && volumes.length > 0) {
1419 return volumes[0].getPath();
1426 * Method that applies the current theme to the activity
1428 public void applyTheme() {
1430 if (getBreadcrumb() != null) {
1431 getBreadcrumb().applyTheme();
1434 //- Redraw the adapter view
1435 Theme theme = ThemeManager.getCurrentTheme(getContext());
1436 theme.setBackgroundDrawable(getContext(), this, "background_drawable"); //$NON-NLS-1$
1437 if (this.mAdapter != null) {
1438 this.mAdapter.notifyThemeChanged();
1440 if (this.mAdapterView instanceof ListView) {
1441 ((ListView)this.mAdapterView).setDivider(
1442 theme.getDrawable(getContext(), "horizontal_divider_drawable")); //$NON-NLS-1$