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 //Check that it is really necessary change the directory
249 if (!mReload && NavigationView.this.mCurrentDir != null &&
250 NavigationView.this.mCurrentDir.compareTo(mNewDirChecked) == 0) {
254 mHasChanged = !(NavigationView.this.mCurrentDir != null &&
255 NavigationView.this.mCurrentDir.compareTo(mNewDirChecked) == 0);
256 mIsNewHistory = (NavigationView.this.mCurrentDir != null);
259 //Reset the custom title view and returns to breadcrumb
260 if (NavigationView.this.mTitle != null) {
261 NavigationView.this.mTitle.post(new Runnable() {
265 NavigationView.this.mTitle.restoreView();
266 } catch (Exception e) {
274 //Start of loading data
275 if (NavigationView.this.mBreadcrumb != null) {
277 NavigationView.this.mBreadcrumb.startLoading();
278 } catch (Throwable ex) {
283 //Get the files, resolve links and apply configuration
284 //(sort, hidden, ...)
285 List<FileSystemObject> files = NavigationView.this.mFiles;
287 files = CommandHelper.listFiles(getContext(), mNewDirChecked, null);
290 //Apply user preferences
291 List<FileSystemObject> sortedFiles =
292 FileHelper.applyUserPreferences(files, this.mRestrictions, this.mChRooted);
296 } catch (final ConsoleAllocException e) {
297 //Show exception and exists
298 NavigationView.this.post(new Runnable() {
301 Context ctx = getContext();
302 Log.e(TAG, ctx.getString(
303 R.string.msgs_cant_create_console), e);
304 DialogHelper.showToast(ctx,
305 R.string.msgs_cant_create_console,
307 ((Activity)ctx).finish();
311 } catch (Exception ex) {
312 //End of loading data
313 if (NavigationView.this.mBreadcrumb != null) {
315 NavigationView.this.mBreadcrumb.endLoading();
316 } catch (Throwable ex2) {
320 if (ex instanceof CancelledOperationException) {
324 //Capture exception (attach task, and use listener to do the anim)
325 ExceptionUtil.attachAsyncTask(
327 new AsyncTask<Object, Integer, Boolean>() {
328 private List<FileSystemObject> mTaskFiles = null;
331 "unchecked", "unqualified-field-access"
333 protected Boolean doInBackground(Object... taskParams) {
334 mTaskFiles = (List<FileSystemObject>)taskParams[0];
339 @SuppressWarnings("unqualified-field-access")
340 protected void onPostExecute(Boolean result) {
341 if (!result.booleanValue()) {
345 mTaskFiles, mAddToHistory, mIsNewHistory, mHasChanged,
346 mSearchInfo, mNewDirChecked, mScrollTo);
349 final OnRelaunchCommandResult exListener =
350 new OnRelaunchCommandResult() {
352 public void onSuccess() {
356 public void onFailed(Throwable cause) {
360 public void onCancelled() {
363 private void done() {
368 ExceptionUtil.translateException(
369 getContext(), ex, false, true, exListener);
378 protected void onCancelled(List<FileSystemObject> result) {
386 protected void onPostExecute(List<FileSystemObject> files) {
387 // This means an exception. This method will be recalled then
389 onPostExecuteTask(files, mAddToHistory, mIsNewHistory, mHasChanged,
390 mSearchInfo, mNewDirChecked, mScrollTo);
398 * Method that performs a fade animation.
400 * @param out Fade out (true); Fade in (false)
402 void fadeEfect(final boolean out) {
403 Activity activity = (Activity)getContext();
404 activity.runOnUiThread(new Runnable() {
407 Animation fadeAnim = out ?
408 new AlphaAnimation(1, 0) :
409 new AlphaAnimation(0, 1);
410 fadeAnim.setDuration(50L);
411 fadeAnim.setFillAfter(true);
412 fadeAnim.setInterpolator(new AccelerateInterpolator());
413 NavigationView.this.startAnimation(fadeAnim);
420 private String mCurrentDir;
421 private NavigationLayoutMode mCurrentMode;
425 List<FileSystemObject> mFiles;
426 private FileSystemObjectAdapter mAdapter;
428 private OnHistoryListener mOnHistoryListener;
429 private OnNavigationSelectionChangedListener mOnNavigationSelectionChangedListener;
430 private OnNavigationRequestMenuListener mOnNavigationRequestMenuListener;
431 private OnFilePickedListener mOnFilePickedListener;
432 private OnDirectoryChangedListener mOnDirectoryChangedListener;
434 private boolean mChRooted;
436 private NAVIGATION_MODE mNavigationMode;
439 private Map<DisplayRestrictions, Object> mRestrictions;
441 private NavigationTask mNavigationTask;
446 Breadcrumb mBreadcrumb;
450 NavigationCustomTitleView mTitle;
454 AdapterView<?> mAdapterView;
456 //The layout for icons mode
457 private static final int RESOURCE_MODE_ICONS_LAYOUT = R.layout.navigation_view_icons;
458 private static final int RESOURCE_MODE_ICONS_ITEM = R.layout.navigation_view_icons_item;
459 //The layout for simple mode
460 private static final int RESOURCE_MODE_SIMPLE_LAYOUT = R.layout.navigation_view_simple;
461 private static final int RESOURCE_MODE_SIMPLE_ITEM = R.layout.navigation_view_simple_item;
462 //The layout for details mode
463 private static final int RESOURCE_MODE_DETAILS_LAYOUT = R.layout.navigation_view_details;
464 private static final int RESOURCE_MODE_DETAILS_ITEM = R.layout.navigation_view_details_item;
466 //The current layout identifier (is shared for all the mode layout)
467 private static final int RESOURCE_CURRENT_LAYOUT = R.id.navigation_view_layout;
470 * Constructor of <code>NavigationView</code>.
472 * @param context The current context
473 * @param attrs The attributes of the XML tag that is inflating the view.
475 public NavigationView(Context context, AttributeSet attrs) {
476 super(context, attrs);
477 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Navigable);
486 * Constructor of <code>NavigationView</code>.
488 * @param context The current context
489 * @param attrs The attributes of the XML tag that is inflating the view.
490 * @param defStyle The default style to apply to this view. If 0, no style
491 * will be applied (beyond what is included in the theme). This may
492 * either be an attribute resource, whose value will be retrieved
493 * from the current theme, or an explicit style resource.
495 public NavigationView(Context context, AttributeSet attrs, int defStyle) {
496 super(context, attrs, defStyle);
497 TypedArray a = context.obtainStyledAttributes(
498 attrs, R.styleable.Navigable, defStyle, 0);
507 * Invoked when the instance need to be saved.
509 * @return NavigationViewInfoParcelable The serialized info
511 public NavigationViewInfoParcelable onSaveState() {
512 //Return the persistent the data
513 NavigationViewInfoParcelable parcel = new NavigationViewInfoParcelable();
514 parcel.setId(this.mId);
515 parcel.setCurrentDir(this.mCurrentDir);
516 parcel.setChRooted(this.mChRooted);
517 parcel.setSelectedFiles(this.mAdapter.getSelectedItems());
518 parcel.setFiles(this.mFiles);
520 int firstVisiblePosition = mAdapterView.getFirstVisiblePosition();
521 if (firstVisiblePosition >= 0 && firstVisiblePosition < mAdapter.getCount()) {
522 FileSystemObject firstVisible = mAdapter
523 .getItem(firstVisiblePosition);
524 parcel.setFirstVisible(firstVisible);
531 * Invoked when the instance need to be restored.
533 * @param info The serialized info
534 * @return boolean If can restore
536 public boolean onRestoreState(NavigationViewInfoParcelable info) {
538 this.mId = info.getId();
539 this.mCurrentDir = info.getCurrentDir();
540 this.mChRooted = info.getChRooted();
541 this.mFiles = info.getFiles();
542 this.mAdapter.setSelectedItems(info.getSelectedFiles());
544 final FileSystemObject firstVisible = info.getFirstVisible();
547 refresh(firstVisible);
552 * Method that initializes the view. This method loads all the necessary
553 * information and create an appropriate layout for the view.
555 * @param tarray The type array
557 private void init(TypedArray tarray) {
559 this.mNavigationMode = NAVIGATION_MODE.BROWSABLE;
560 int mode = tarray.getInteger(
561 R.styleable.Navigable_navigation,
562 NAVIGATION_MODE.BROWSABLE.ordinal());
563 if (mode >= 0 && mode < NAVIGATION_MODE.values().length) {
564 this.mNavigationMode = NAVIGATION_MODE.values()[mode];
567 // Initialize default restrictions (no restrictions)
568 this.mRestrictions = new HashMap<DisplayRestrictions, Object>();
570 //Initialize variables
571 this.mFiles = new ArrayList<FileSystemObject>();
573 // Is ChRooted environment?
574 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
575 // Pick mode is always ChRooted
576 this.mChRooted = true;
579 FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
582 //Retrieve the default configuration
583 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
584 SharedPreferences preferences = Preferences.getSharedPreferences();
585 int viewMode = preferences.getInt(
586 FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
587 ((ObjectIdentifier)FileManagerSettings.
588 SETTINGS_LAYOUT_MODE.getDefaultValue()).getId());
589 changeViewMode(NavigationLayoutMode.fromId(viewMode));
591 // Pick mode has always a details layout
592 changeViewMode(NavigationLayoutMode.DETAILS);
597 * Method that returns the display restrictions to apply to this view.
599 * @return Map<DisplayRestrictions, Object> The restrictions to apply
601 public Map<DisplayRestrictions, Object> getRestrictions() {
602 return this.mRestrictions;
606 * Method that sets the display restrictions to apply to this view.
608 * @param mRestrictions The restrictions to apply
610 public void setRestrictions(Map<DisplayRestrictions, Object> mRestrictions) {
611 this.mRestrictions = mRestrictions;
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> getFiles() {
620 if (this.mFiles == null) {
623 return new ArrayList<FileSystemObject>(this.mFiles);
627 * Method that returns the current file list of the navigation view.
629 * @return List<FileSystemObject> The current file list of the navigation view
631 public List<FileSystemObject> getSelectedFiles() {
632 if (this.mAdapter != null && this.mAdapter.getSelectedItems() != null) {
633 return new ArrayList<FileSystemObject>(this.mAdapter.getSelectedItems());
639 * Method that returns the custom title fragment associated with this navigation view.
641 * @return NavigationCustomTitleView The custom title view fragment
643 public NavigationCustomTitleView getCustomTitle() {
648 * Method that associates the custom title fragment with this navigation view.
650 * @param title The custom title view fragment
652 public void setCustomTitle(NavigationCustomTitleView title) {
657 * Method that returns the breadcrumb associated with this navigation view.
659 * @return Breadcrumb The breadcrumb view fragment
661 public Breadcrumb getBreadcrumb() {
662 return this.mBreadcrumb;
666 * Method that associates the breadcrumb with this navigation view.
668 * @param breadcrumb The breadcrumb view fragment
670 public void setBreadcrumb(Breadcrumb breadcrumb) {
671 this.mBreadcrumb = breadcrumb;
672 this.mBreadcrumb.addBreadcrumbListener(this);
676 * Method that sets the listener for communicate history changes.
678 * @param onHistoryListener The listener for communicate history changes
680 public void setOnHistoryListener(OnHistoryListener onHistoryListener) {
681 this.mOnHistoryListener = onHistoryListener;
685 * Method that sets the listener which communicates selection changes.
687 * @param onNavigationSelectionChangedListener The listener reference
689 public void setOnNavigationSelectionChangedListener(
690 OnNavigationSelectionChangedListener onNavigationSelectionChangedListener) {
691 this.mOnNavigationSelectionChangedListener = onNavigationSelectionChangedListener;
695 * Method that sets the listener for menu item requests.
697 * @param onNavigationRequestMenuListener The listener reference
699 public void setOnNavigationOnRequestMenuListener(
700 OnNavigationRequestMenuListener onNavigationRequestMenuListener) {
701 this.mOnNavigationRequestMenuListener = onNavigationRequestMenuListener;
705 * @return the mOnFilePickedListener
707 public OnFilePickedListener getOnFilePickedListener() {
708 return this.mOnFilePickedListener;
712 * Method that sets the listener for picked items
714 * @param onFilePickedListener The listener reference
716 public void setOnFilePickedListener(OnFilePickedListener onFilePickedListener) {
717 this.mOnFilePickedListener = onFilePickedListener;
721 * Method that sets the listener for directory changes
723 * @param onDirectoryChangedListener The listener reference
725 public void setOnDirectoryChangedListener(
726 OnDirectoryChangedListener onDirectoryChangedListener) {
727 this.mOnDirectoryChangedListener = onDirectoryChangedListener;
731 * Method that sets if the view should use flinger gesture detection.
733 * @param useFlinger If the view should use flinger gesture detection
735 public void setUseFlinger(boolean useFlinger) {
736 if (this.mCurrentMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
740 // Set the flinger listener (only when navigate)
741 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
742 if (this.mAdapterView instanceof FlingerListView) {
744 ((FlingerListView)this.mAdapterView).
745 setOnItemFlingerListener(this.mOnItemFlingerListener);
747 ((FlingerListView)this.mAdapterView).setOnItemFlingerListener(null);
754 * Method that forces the view to scroll to the file system object passed.
756 * @param fso The file system object
758 public void scrollTo(final FileSystemObject fso) {
760 this.mAdapterView.post(new Runnable() {
766 int position = mAdapter.getPosition(fso);
767 mAdapterView.setSelection(position);
769 // Make the scrollbar appear
771 mAdapterView.scrollBy(0, 1);
772 mAdapterView.scrollBy(0, -1);
775 } catch (Exception e) {
776 mAdapterView.setSelection(0);
779 mAdapterView.setSelection(0);
787 * Method that refresh the view data.
789 public void refresh() {
794 * Method that refresh the view data.
796 * @param restore Restore previous position
798 public void refresh(boolean restore) {
799 FileSystemObject fso = null;
800 // Try to restore the previous scroll position
803 if (this.mAdapterView != null && this.mAdapter != null) {
804 int position = this.mAdapterView.getFirstVisiblePosition();
805 fso = this.mAdapter.getItem(position);
807 } catch (Throwable _throw) {/**NON BLOCK**/}
813 * Method that refresh the view data.
815 * @param scrollTo Scroll to object
817 public void refresh(FileSystemObject scrollTo) {
818 //Check that current directory was set
819 if (this.mCurrentDir == null || this.mFiles == null || this.mNavigationTask != null) {
824 changeCurrentDir(this.mCurrentDir, false, true, false, null, scrollTo);
828 * Method that recycles this object
830 public void recycle() {
831 if (this.mAdapter != null) {
832 this.mAdapter.dispose();
837 * Method that change the view mode.
839 * @param newMode The new mode
841 @SuppressWarnings("unchecked")
842 public void changeViewMode(final NavigationLayoutMode newMode) {
843 //Check that it is really necessary change the mode
844 if (this.mCurrentMode != null && this.mCurrentMode.compareTo(newMode) == 0) {
848 // If we should set the listview to response to flinger gesture detection
850 Preferences.getSharedPreferences().getBoolean(
851 FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
852 ((Boolean)FileManagerSettings.
853 SETTINGS_USE_FLINGER.
854 getDefaultValue()).booleanValue());
856 //Creates the new layout
857 AdapterView<ListAdapter> newView = null;
858 int itemResourceId = -1;
859 if (newMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
860 newView = (AdapterView<ListAdapter>)inflate(
861 getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
862 itemResourceId = RESOURCE_MODE_ICONS_ITEM;
864 } else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
865 newView = (AdapterView<ListAdapter>)inflate(
866 getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
867 itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
869 // Set the flinger listener (only when navigate)
870 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
871 if (useFlinger && newView instanceof FlingerListView) {
872 ((FlingerListView)newView).
873 setOnItemFlingerListener(this.mOnItemFlingerListener);
877 } else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
878 newView = (AdapterView<ListAdapter>)inflate(
879 getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
880 itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
882 // Set the flinger listener (only when navigate)
883 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
884 if (useFlinger && newView instanceof FlingerListView) {
885 ((FlingerListView)newView).
886 setOnItemFlingerListener(this.mOnItemFlingerListener);
891 //Get the current adapter and its adapter list
892 List<FileSystemObject> files = new ArrayList<FileSystemObject>(this.mFiles);
893 final AdapterView<ListAdapter> current =
894 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
895 FileSystemObjectAdapter adapter =
896 new FileSystemObjectAdapter(
898 new ArrayList<FileSystemObject>(),
900 this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0);
901 adapter.setOnSelectionChangedListener(this);
903 //Remove current layout
904 if (current != null) {
905 if (current.getAdapter() != null) {
906 //Save selected items before dispose adapter
907 FileSystemObjectAdapter currentAdapter =
908 ((FileSystemObjectAdapter)current.getAdapter());
909 adapter.setSelectedItems(currentAdapter.getSelectedItems());
910 currentAdapter.dispose();
915 adapter.addAll(files);
918 this.mAdapter = adapter;
919 newView.setAdapter(this.mAdapter);
920 newView.setOnItemClickListener(NavigationView.this);
923 this.mAdapterView = newView;
925 this.mCurrentMode = newMode;
927 // Pick mode doesn't implements the onlongclick
928 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
929 this.mAdapterView.setOnItemLongClickListener(this);
931 this.mAdapterView.setOnItemLongClickListener(null);
934 //Save the preference (only in navigation browse mode)
935 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
937 Preferences.savePreference(
938 FileManagerSettings.SETTINGS_LAYOUT_MODE, newMode, true);
939 } catch (Exception ex) {
940 Log.e(TAG, "Save of view mode preference fails", ex); //$NON-NLS-1$
946 * Method that removes a {@link FileSystemObject} from the view
948 * @param fso The file system object
950 public void removeItem(FileSystemObject fso) {
951 // Delete also from internal list
953 int cc = this.mFiles.size()-1;
954 for (int i = cc; i >= 0; i--) {
955 FileSystemObject f = this.mFiles.get(i);
956 if (f != null && f.compareTo(fso) == 0) {
957 this.mFiles.remove(i);
962 this.mAdapter.remove(fso);
966 * Method that removes a file system object from his path from the view
968 * @param path The file system object path
970 public void removeItem(String path) {
971 FileSystemObject fso = this.mAdapter.getItem(path);
973 this.mAdapter.remove(fso);
978 * Method that returns the current directory.
980 * @return String The current directory
982 public String getCurrentDir() {
983 return this.mCurrentDir;
987 * Method that changes the current directory of the view.
989 * @param newDir The new directory location
991 public void changeCurrentDir(final String newDir) {
992 changeCurrentDir(newDir, true, false, false, null, null);
996 * Method that changes the current directory of the view.
998 * @param newDir The new directory location
999 * @param addToHistory Add the directory to history
1001 public void changeCurrentDir(final String newDir, boolean addToHistory) {
1002 changeCurrentDir(newDir, addToHistory, false, false, null, null);
1006 * Method that changes the current directory of the view.
1008 * @param newDir The new directory location
1009 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1011 public void changeCurrentDir(final String newDir, SearchInfoParcelable searchInfo) {
1012 changeCurrentDir(newDir, true, false, false, searchInfo, null);
1016 * Method that changes the current directory of the view.
1018 * @param newDir The new directory location
1019 * @param addToHistory Add the directory to history
1020 * @param reload Force the reload of the data
1021 * @param useCurrent If this method must use the actual data (for back actions)
1022 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1023 * @param scrollTo If not null, then listview must scroll to this item
1025 private void changeCurrentDir(
1026 final String newDir, final boolean addToHistory,
1027 final boolean reload, final boolean useCurrent,
1028 final SearchInfoParcelable searchInfo, final FileSystemObject scrollTo) {
1029 mNavigationTask = new NavigationTask(useCurrent, addToHistory, reload,
1030 searchInfo, scrollTo, mRestrictions, mChRooted);
1031 mNavigationTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, newDir);
1035 * Remove all unmounted files in the current selection
1037 public void removeUnmountedSelection() {
1038 List<FileSystemObject> selection = mAdapter.getSelectedItems();
1039 int cc = selection.size() - 1;
1040 for (int i = cc; i >= 0; i--) {
1041 FileSystemObject item = selection.get(i);
1042 VirtualMountPointConsole vc =
1043 VirtualMountPointConsole.getVirtualConsoleForPath(item.getFullPath());
1044 if (vc != null && !vc.isMounted()) {
1045 selection.remove(i);
1048 mAdapter.setSelectedItems(selection);
1049 mAdapter.notifyDataSetChanged();
1051 // Do not call the selection listener. This method is supposed to be called by the
1057 * Method invoked when a execution ends.
1059 * @param files The files obtains from the list
1060 * @param addToHistory If add path to history
1061 * @param isNewHistory If is new history
1062 * @param hasChanged If current directory was changed
1063 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1064 * @param newDir The new directory
1065 * @param scrollTo If not null, then listview must scroll to this item
1068 void onPostExecuteTask(
1069 List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
1070 boolean hasChanged, SearchInfoParcelable searchInfo,
1071 String newDir, final FileSystemObject scrollTo) {
1073 //Check that there is not errors and have some data
1074 if (files == null) {
1078 //Remove parent directory if we are in the root of a chrooted environment
1079 if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
1080 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
1086 if (addToHistory && hasChanged && isNewHistory) {
1087 if (this.mOnHistoryListener != null) {
1088 //Communicate the need of a history change
1089 this.mOnHistoryListener.onNewHistory(onSaveState());
1095 this.mFiles = files;
1096 if (searchInfo != null) {
1097 searchInfo.setSuccessNavigation(true);
1100 //Change the breadcrumb
1101 if (this.mBreadcrumb != null) {
1102 this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
1105 //If scrollTo is null, the position will be set to 0
1108 //The current directory is now the "newDir"
1109 this.mCurrentDir = newDir;
1110 if (this.mOnDirectoryChangedListener != null) {
1111 FileSystemObject dir = FileHelper.createFileSystemObject(new File(newDir));
1112 this.mOnDirectoryChangedListener.onDirectoryChanged(dir);
1115 mNavigationTask = null;
1117 //If calling activity is search, then save the search history
1118 if (searchInfo != null) {
1119 this.mOnHistoryListener.onNewHistory(searchInfo);
1122 //End of loading data
1124 NavigationView.this.mBreadcrumb.endLoading();
1125 } catch (Throwable ex) {
1132 * Method that loads the files in the adapter.
1134 * @param files The files to load in the adapter
1137 @SuppressWarnings("unchecked")
1138 private void loadData(final List<FileSystemObject> files) {
1139 //Notify data to adapter view
1140 final AdapterView<ListAdapter> view =
1141 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
1142 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
1143 adapter.setNotifyOnChange(false);
1145 adapter.addAll(files);
1146 adapter.notifyDataSetChanged();
1153 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1154 // Different actions depending on user preference
1156 // Get the adapter and the fso
1157 FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
1158 if (adapter == null || position < 0 || (position >= adapter.getCount())) {
1161 FileSystemObject fso = adapter.getItem(position);
1163 // Parent directory hasn't actions
1164 if (fso instanceof ParentDirectory) {
1168 // Pick mode doesn't implements the onlongclick
1169 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
1174 return true; //Always consume the event
1178 * Method that opens or navigates to the {@link FileSystemObject}
1180 * @param fso The file system object
1182 public void open(FileSystemObject fso) {
1187 * Method that opens or navigates to the {@link FileSystemObject}
1189 * @param fso The file system object
1190 * @param searchInfo The search info
1192 public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
1193 // If is a folder, then navigate to
1194 if (FileHelper.isDirectory(fso)) {
1195 changeCurrentDir(fso.getFullPath(), searchInfo);
1197 // Open the file with the preferred registered app
1198 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1206 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1208 FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
1209 if (fso instanceof ParentDirectory) {
1210 changeCurrentDir(fso.getParent(), true, false, false, null, null);
1212 } else if (fso instanceof Directory) {
1213 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
1215 } else if (fso instanceof Symlink) {
1216 Symlink symlink = (Symlink)fso;
1217 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
1219 symlink.getLinkRef().getFullPath(), true, false, false, null, null);
1223 // Open the link ref
1224 fso = symlink.getLinkRef();
1227 // Open the file (edit or pick)
1228 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
1229 // Open the file with the preferred registered app
1230 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1232 // Request a file pick selection
1233 if (this.mOnFilePickedListener != null) {
1234 this.mOnFilePickedListener.onFilePicked(fso);
1237 } catch (Throwable ex) {
1238 ExceptionUtil.translateException(getContext(), ex);
1246 public void onRequestRefresh(Object o, boolean clearSelection) {
1247 if (o instanceof FileSystemObject) {
1248 refresh((FileSystemObject)o);
1249 } else if (o == null) {
1252 if (clearSelection) {
1261 public void onRequestBookmarksRefresh() {
1269 public void onRequestRemove(Object o, boolean clearSelection) {
1270 if (o != null && o instanceof FileSystemObject) {
1271 removeItem((FileSystemObject)o);
1273 onRequestRefresh(null, clearSelection);
1275 if (clearSelection) {
1284 public void onNavigateTo(Object o) {
1289 public void onCancel() {
1297 public void onBreadcrumbItemClick(BreadcrumbItem item) {
1298 changeCurrentDir(item.getItemPath(), true, true, false, null, null);
1305 public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
1306 if (this.mOnNavigationSelectionChangedListener != null) {
1307 this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
1312 * Method invoked when a request to show the menu associated
1313 * with an item is started.
1315 * @param item The item for which the request was started
1317 public void onRequestMenu(final FileSystemObject item) {
1318 if (this.mOnNavigationRequestMenuListener != null) {
1319 this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
1327 public void onToggleSelection(FileSystemObject fso) {
1328 if (this.mAdapter != null) {
1329 this.mAdapter.toggleSelection(fso);
1337 public void onDeselectAll() {
1338 if (this.mAdapter != null) {
1339 this.mAdapter.deselectedAll();
1347 public void onSelectAllVisibleItems() {
1348 if (this.mAdapter != null) {
1349 this.mAdapter.selectedAllVisibleItems();
1357 public void onDeselectAllVisibleItems() {
1358 if (this.mAdapter != null) {
1359 this.mAdapter.deselectedAllVisibleItems();
1367 public List<FileSystemObject> onRequestSelectedFiles() {
1368 return this.getSelectedFiles();
1375 public List<FileSystemObject> onRequestCurrentItems() {
1376 return this.getFiles();
1383 public String onRequestCurrentDir() {
1384 return this.mCurrentDir;
1388 * Method that creates a ChRooted environment, protecting the user to break anything
1392 public void createChRooted() {
1393 // If we are in a ChRooted environment, then do nothing
1394 if (this.mChRooted) return;
1395 this.mChRooted = true;
1397 //Change to first storage volume
1398 StorageVolume[] volumes =
1399 StorageHelper.getStorageVolumes(getContext(), false);
1400 if (volumes != null && volumes.length > 0) {
1401 changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1406 * Method that exits from a ChRooted environment
1409 public void exitChRooted() {
1410 // If we aren't in a ChRooted environment, then do nothing
1411 if (!this.mChRooted) return;
1412 this.mChRooted = false;
1419 * Method that ensures that the user don't go outside the ChRooted environment
1421 * @param newDir The new directory to navigate to
1424 private String checkChRootedNavigation(String newDir) {
1425 // If we aren't in ChRooted environment, then there is nothing to check
1426 if (!this.mChRooted) return newDir;
1428 // Check if the path is owned by one of the storage volumes
1429 if (!StorageHelper.isPathInStorageVolume(newDir)) {
1430 StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext(), false);
1431 if (volumes != null && volumes.length > 0) {
1432 return volumes[0].getPath();
1439 * Method that applies the current theme to the activity
1441 public void applyTheme() {
1443 if (getBreadcrumb() != null) {
1444 getBreadcrumb().applyTheme();
1447 //- Redraw the adapter view
1448 Theme theme = ThemeManager.getCurrentTheme(getContext());
1449 theme.setBackgroundDrawable(getContext(), this, "background_drawable"); //$NON-NLS-1$
1450 if (this.mAdapter != null) {
1451 this.mAdapter.notifyThemeChanged();
1453 if (this.mAdapterView instanceof ListView) {
1454 ((ListView)this.mAdapterView).setDivider(
1455 theme.getDrawable(getContext(), "horizontal_divider_drawable")); //$NON-NLS-1$