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.ConsoleAllocException;
42 import com.cyanogenmod.filemanager.listeners.OnHistoryListener;
43 import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
44 import com.cyanogenmod.filemanager.listeners.OnSelectionListener;
45 import com.cyanogenmod.filemanager.model.Directory;
46 import com.cyanogenmod.filemanager.model.FileSystemObject;
47 import com.cyanogenmod.filemanager.model.ParentDirectory;
48 import com.cyanogenmod.filemanager.model.Symlink;
49 import com.cyanogenmod.filemanager.parcelables.NavigationViewInfoParcelable;
50 import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
51 import com.cyanogenmod.filemanager.preferences.AccessMode;
52 import com.cyanogenmod.filemanager.preferences.DisplayRestrictions;
53 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
54 import com.cyanogenmod.filemanager.preferences.NavigationLayoutMode;
55 import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
56 import com.cyanogenmod.filemanager.preferences.Preferences;
57 import com.cyanogenmod.filemanager.ui.ThemeManager;
58 import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
59 import com.cyanogenmod.filemanager.ui.policy.DeleteActionPolicy;
60 import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
61 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerListener;
62 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
63 import com.cyanogenmod.filemanager.util.CommandHelper;
64 import com.cyanogenmod.filemanager.util.DialogHelper;
65 import com.cyanogenmod.filemanager.util.ExceptionUtil;
66 import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
67 import com.cyanogenmod.filemanager.util.FileHelper;
68 import com.cyanogenmod.filemanager.util.StorageHelper;
71 import java.util.ArrayList;
72 import java.util.HashMap;
73 import java.util.List;
77 * The file manager implementation view (contains the graphical representation and the input
78 * management for a file manager; shows the folders/files, the mode view, react touch events,
81 public class NavigationView extends RelativeLayout implements
82 AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener,
83 BreadcrumbListener, OnSelectionChangedListener, OnSelectionListener, OnRequestRefreshListener {
85 private static final String TAG = "NavigationView"; //$NON-NLS-1$
88 * An interface to communicate selection changes events.
90 public interface OnNavigationSelectionChangedListener {
92 * Method invoked when the selection changed.
94 * @param navView The navigation view that generate the event
95 * @param selectedItems The new selected items
97 void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems);
101 * An interface to communicate a request for show the menu associated
104 public interface OnNavigationRequestMenuListener {
106 * Method invoked when a request to show the menu associated
107 * with an item is started.
109 * @param navView The navigation view that generate the event
110 * @param item The item for which the request was started
112 void onRequestMenu(NavigationView navView, FileSystemObject item);
116 * An interface to communicate a request when the user choose a file.
118 public interface OnFilePickedListener {
120 * Method invoked when a request when the user choose a file.
122 * @param item The item choose
124 void onFilePicked(FileSystemObject item);
128 * An interface to communicate a change of the current directory
130 public interface OnDirectoryChangedListener {
132 * Method invoked when the current directory changes
134 * @param item The newly active directory
136 void onDirectoryChanged(FileSystemObject item);
140 * The navigation view mode
143 public enum NAVIGATION_MODE {
145 * The navigation view acts as a browser, and allow open files itself.
149 * The navigation view acts as a picker of files
155 * A listener for flinging events from {@link FlingerListView}
157 private final OnItemFlingerListener mOnItemFlingerListener = new OnItemFlingerListener() {
160 public boolean onItemFlingerStart(
161 AdapterView<?> parent, View view, int position, long id) {
163 // Response if the item can be removed
164 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
165 FileSystemObject fso = adapter.getItem(position);
167 if (fso instanceof ParentDirectory) {
172 } catch (Exception e) {
173 ExceptionUtil.translateException(getContext(), e, true, false);
179 public void onItemFlingerEnd(OnItemFlingerResponder responder,
180 AdapterView<?> parent, View view, int position, long id) {
183 // Response if the item can be removed
184 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
185 FileSystemObject fso = adapter.getItem(position);
187 DeleteActionPolicy.removeFileSystemObject(
196 // Cancels the flinger operation
199 } catch (Exception e) {
200 ExceptionUtil.translateException(getContext(), e, true, false);
206 private class NavigationTask extends AsyncTask<String, Integer, List<FileSystemObject>> {
207 private final boolean mUseCurrent;
208 private final boolean mAddToHistory;
209 private final boolean mReload;
210 private boolean mHasChanged;
211 private boolean mIsNewHistory;
212 private String mNewDirChecked;
213 private final SearchInfoParcelable mSearchInfo;
214 private final FileSystemObject mScrollTo;
216 public NavigationTask(boolean useCurrent, boolean addToHistory, boolean reload,
217 SearchInfoParcelable searchInfo, FileSystemObject scrollTo) {
219 this.mUseCurrent = useCurrent;
220 this.mAddToHistory = addToHistory;
221 this.mSearchInfo = searchInfo;
222 this.mReload = reload;
223 this.mScrollTo = scrollTo;
230 protected List<FileSystemObject> doInBackground(String... params) {
231 // Check navigation security (don't allow to go outside the ChRooted environment if one
233 mNewDirChecked = checkChRootedNavigation(params[0]);
235 //Check that it is really necessary change the directory
236 if (!mReload && NavigationView.this.mCurrentDir != null &&
237 NavigationView.this.mCurrentDir.compareTo(mNewDirChecked) == 0) {
241 mHasChanged = !(NavigationView.this.mCurrentDir != null &&
242 NavigationView.this.mCurrentDir.compareTo(mNewDirChecked) == 0);
243 mIsNewHistory = (NavigationView.this.mCurrentDir != null);
246 //Reset the custom title view and returns to breadcrumb
247 if (NavigationView.this.mTitle != null) {
248 NavigationView.this.mTitle.post(new Runnable() {
252 NavigationView.this.mTitle.restoreView();
253 } catch (Exception e) {
261 //Start of loading data
262 if (NavigationView.this.mBreadcrumb != null) {
264 NavigationView.this.mBreadcrumb.startLoading();
265 } catch (Throwable ex) {
270 //Get the files, resolve links and apply configuration
271 //(sort, hidden, ...)
272 List<FileSystemObject> files = NavigationView.this.mFiles;
274 files = CommandHelper.listFiles(getContext(), mNewDirChecked, null);
278 } catch (final ConsoleAllocException e) {
279 //Show exception and exists
280 NavigationView.this.post(new Runnable() {
283 Context ctx = getContext();
284 Log.e(TAG, ctx.getString(
285 R.string.msgs_cant_create_console), e);
286 DialogHelper.showToast(ctx,
287 R.string.msgs_cant_create_console,
289 ((Activity)ctx).finish();
293 } catch (Exception ex) {
294 //End of loading data
295 if (NavigationView.this.mBreadcrumb != null) {
297 NavigationView.this.mBreadcrumb.endLoading();
298 } catch (Throwable ex2) {
303 //Capture exception (attach task, and use listener to do the anim)
304 ExceptionUtil.attachAsyncTask(
306 new AsyncTask<Object, Integer, Boolean>() {
307 private List<FileSystemObject> mTaskFiles = null;
310 "unchecked", "unqualified-field-access"
312 protected Boolean doInBackground(Object... taskParams) {
313 mTaskFiles = (List<FileSystemObject>)taskParams[0];
318 @SuppressWarnings("unqualified-field-access")
319 protected void onPostExecute(Boolean result) {
320 if (!result.booleanValue()) {
324 mTaskFiles, mAddToHistory, mIsNewHistory, mHasChanged,
325 mSearchInfo, mNewDirChecked, mScrollTo);
328 final OnRelaunchCommandResult exListener =
329 new OnRelaunchCommandResult() {
331 public void onSuccess() {
335 public void onFailed(Throwable cause) {
339 public void onCancelled() {
342 private void done() {
347 ExceptionUtil.translateException(
348 getContext(), ex, false, true, exListener);
357 protected void onCancelled(List<FileSystemObject> result) {
365 protected void onPostExecute(List<FileSystemObject> files) {
366 // This means an exception. This method will be recalled then
368 onPostExecuteTask(files, mAddToHistory, mIsNewHistory, mHasChanged,
369 mSearchInfo, mNewDirChecked, mScrollTo);
377 * Method that performs a fade animation.
379 * @param out Fade out (true); Fade in (false)
381 void fadeEfect(final boolean out) {
382 Activity activity = (Activity)getContext();
383 activity.runOnUiThread(new Runnable() {
386 Animation fadeAnim = out ?
387 new AlphaAnimation(1, 0) :
388 new AlphaAnimation(0, 1);
389 fadeAnim.setDuration(50L);
390 fadeAnim.setFillAfter(true);
391 fadeAnim.setInterpolator(new AccelerateInterpolator());
392 NavigationView.this.startAnimation(fadeAnim);
399 private String mCurrentDir;
400 private NavigationLayoutMode mCurrentMode;
404 List<FileSystemObject> mFiles;
405 private FileSystemObjectAdapter mAdapter;
407 private OnHistoryListener mOnHistoryListener;
408 private OnNavigationSelectionChangedListener mOnNavigationSelectionChangedListener;
409 private OnNavigationRequestMenuListener mOnNavigationRequestMenuListener;
410 private OnFilePickedListener mOnFilePickedListener;
411 private OnDirectoryChangedListener mOnDirectoryChangedListener;
413 private boolean mChRooted;
415 private NAVIGATION_MODE mNavigationMode;
418 private Map<DisplayRestrictions, Object> mRestrictions;
423 Breadcrumb mBreadcrumb;
427 NavigationCustomTitleView mTitle;
431 AdapterView<?> mAdapterView;
433 //The layout for icons mode
434 private static final int RESOURCE_MODE_ICONS_LAYOUT = R.layout.navigation_view_icons;
435 private static final int RESOURCE_MODE_ICONS_ITEM = R.layout.navigation_view_icons_item;
436 //The layout for simple mode
437 private static final int RESOURCE_MODE_SIMPLE_LAYOUT = R.layout.navigation_view_simple;
438 private static final int RESOURCE_MODE_SIMPLE_ITEM = R.layout.navigation_view_simple_item;
439 //The layout for details mode
440 private static final int RESOURCE_MODE_DETAILS_LAYOUT = R.layout.navigation_view_details;
441 private static final int RESOURCE_MODE_DETAILS_ITEM = R.layout.navigation_view_details_item;
443 //The current layout identifier (is shared for all the mode layout)
444 private static final int RESOURCE_CURRENT_LAYOUT = R.id.navigation_view_layout;
447 * Constructor of <code>NavigationView</code>.
449 * @param context The current context
450 * @param attrs The attributes of the XML tag that is inflating the view.
452 public NavigationView(Context context, AttributeSet attrs) {
453 super(context, attrs);
454 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Navigable);
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.
467 * @param defStyle The default style to apply to this view. If 0, no style
468 * will be applied (beyond what is included in the theme). This may
469 * either be an attribute resource, whose value will be retrieved
470 * from the current theme, or an explicit style resource.
472 public NavigationView(Context context, AttributeSet attrs, int defStyle) {
473 super(context, attrs, defStyle);
474 TypedArray a = context.obtainStyledAttributes(
475 attrs, R.styleable.Navigable, defStyle, 0);
484 * Invoked when the instance need to be saved.
486 * @return NavigationViewInfoParcelable The serialized info
488 public NavigationViewInfoParcelable onSaveState() {
489 //Return the persistent the data
490 NavigationViewInfoParcelable parcel = new NavigationViewInfoParcelable();
491 parcel.setId(this.mId);
492 parcel.setCurrentDir(this.mCurrentDir);
493 parcel.setChRooted(this.mChRooted);
494 parcel.setSelectedFiles(this.mAdapter.getSelectedItems());
495 parcel.setFiles(this.mFiles);
497 int firstVisiblePosition = mAdapterView.getFirstVisiblePosition();
498 if (firstVisiblePosition >= 0 && firstVisiblePosition < mAdapter.getCount()) {
499 FileSystemObject firstVisible = mAdapter
500 .getItem(firstVisiblePosition);
501 parcel.setFirstVisible(firstVisible);
508 * Invoked when the instance need to be restored.
510 * @param info The serialized info
511 * @return boolean If can restore
513 public boolean onRestoreState(NavigationViewInfoParcelable info) {
515 this.mId = info.getId();
516 this.mCurrentDir = info.getCurrentDir();
517 this.mChRooted = info.getChRooted();
518 this.mFiles = info.getFiles();
519 this.mAdapter.setSelectedItems(info.getSelectedFiles());
521 final FileSystemObject firstVisible = info.getFirstVisible();
524 refresh(firstVisible);
529 * Method that initializes the view. This method loads all the necessary
530 * information and create an appropriate layout for the view.
532 * @param tarray The type array
534 private void init(TypedArray tarray) {
536 this.mNavigationMode = NAVIGATION_MODE.BROWSABLE;
537 int mode = tarray.getInteger(
538 R.styleable.Navigable_navigation,
539 NAVIGATION_MODE.BROWSABLE.ordinal());
540 if (mode >= 0 && mode < NAVIGATION_MODE.values().length) {
541 this.mNavigationMode = NAVIGATION_MODE.values()[mode];
544 // Initialize default restrictions (no restrictions)
545 this.mRestrictions = new HashMap<DisplayRestrictions, Object>();
547 //Initialize variables
548 this.mFiles = new ArrayList<FileSystemObject>();
550 // Is ChRooted environment?
551 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
552 // Pick mode is always ChRooted
553 this.mChRooted = true;
556 FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
559 //Retrieve the default configuration
560 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
561 SharedPreferences preferences = Preferences.getSharedPreferences();
562 int viewMode = preferences.getInt(
563 FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
564 ((ObjectIdentifier)FileManagerSettings.
565 SETTINGS_LAYOUT_MODE.getDefaultValue()).getId());
566 changeViewMode(NavigationLayoutMode.fromId(viewMode));
568 // Pick mode has always a details layout
569 changeViewMode(NavigationLayoutMode.DETAILS);
574 * Method that returns the display restrictions to apply to this view.
576 * @return Map<DisplayRestrictions, Object> The restrictions to apply
578 public Map<DisplayRestrictions, Object> getRestrictions() {
579 return this.mRestrictions;
583 * Method that sets the display restrictions to apply to this view.
585 * @param mRestrictions The restrictions to apply
587 public void setRestrictions(Map<DisplayRestrictions, Object> mRestrictions) {
588 this.mRestrictions = mRestrictions;
592 * Method that returns the current file list of the navigation view.
594 * @return List<FileSystemObject> The current file list of the navigation view
596 public List<FileSystemObject> getFiles() {
597 if (this.mFiles == null) {
600 return new ArrayList<FileSystemObject>(this.mFiles);
604 * Method that returns the current file list of the navigation view.
606 * @return List<FileSystemObject> The current file list of the navigation view
608 public List<FileSystemObject> getSelectedFiles() {
609 if (this.mAdapter != null && this.mAdapter.getSelectedItems() != null) {
610 return new ArrayList<FileSystemObject>(this.mAdapter.getSelectedItems());
616 * Method that returns the custom title fragment associated with this navigation view.
618 * @return NavigationCustomTitleView The custom title view fragment
620 public NavigationCustomTitleView getCustomTitle() {
625 * Method that associates the custom title fragment with this navigation view.
627 * @param title The custom title view fragment
629 public void setCustomTitle(NavigationCustomTitleView title) {
634 * Method that returns the breadcrumb associated with this navigation view.
636 * @return Breadcrumb The breadcrumb view fragment
638 public Breadcrumb getBreadcrumb() {
639 return this.mBreadcrumb;
643 * Method that associates the breadcrumb with this navigation view.
645 * @param breadcrumb The breadcrumb view fragment
647 public void setBreadcrumb(Breadcrumb breadcrumb) {
648 this.mBreadcrumb = breadcrumb;
649 this.mBreadcrumb.addBreadcrumbListener(this);
653 * Method that sets the listener for communicate history changes.
655 * @param onHistoryListener The listener for communicate history changes
657 public void setOnHistoryListener(OnHistoryListener onHistoryListener) {
658 this.mOnHistoryListener = onHistoryListener;
662 * Method that sets the listener which communicates selection changes.
664 * @param onNavigationSelectionChangedListener The listener reference
666 public void setOnNavigationSelectionChangedListener(
667 OnNavigationSelectionChangedListener onNavigationSelectionChangedListener) {
668 this.mOnNavigationSelectionChangedListener = onNavigationSelectionChangedListener;
672 * Method that sets the listener for menu item requests.
674 * @param onNavigationRequestMenuListener The listener reference
676 public void setOnNavigationOnRequestMenuListener(
677 OnNavigationRequestMenuListener onNavigationRequestMenuListener) {
678 this.mOnNavigationRequestMenuListener = onNavigationRequestMenuListener;
682 * @return the mOnFilePickedListener
684 public OnFilePickedListener getOnFilePickedListener() {
685 return this.mOnFilePickedListener;
689 * Method that sets the listener for picked items
691 * @param onFilePickedListener The listener reference
693 public void setOnFilePickedListener(OnFilePickedListener onFilePickedListener) {
694 this.mOnFilePickedListener = onFilePickedListener;
698 * Method that sets the listener for directory changes
700 * @param onDirectoryChangedListener The listener reference
702 public void setOnDirectoryChangedListener(
703 OnDirectoryChangedListener onDirectoryChangedListener) {
704 this.mOnDirectoryChangedListener = onDirectoryChangedListener;
708 * Method that sets if the view should use flinger gesture detection.
710 * @param useFlinger If the view should use flinger gesture detection
712 public void setUseFlinger(boolean useFlinger) {
713 if (this.mCurrentMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
717 // Set the flinger listener (only when navigate)
718 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
719 if (this.mAdapterView instanceof FlingerListView) {
721 ((FlingerListView)this.mAdapterView).
722 setOnItemFlingerListener(this.mOnItemFlingerListener);
724 ((FlingerListView)this.mAdapterView).setOnItemFlingerListener(null);
731 * Method that forces the view to scroll to the file system object passed.
733 * @param fso The file system object
735 public void scrollTo(final FileSystemObject fso) {
737 this.mAdapterView.post(new Runnable() {
743 int position = mAdapter.getPosition(fso);
744 mAdapterView.setSelection(position);
746 // Make the scrollbar appear
748 mAdapterView.scrollBy(0, 1);
749 mAdapterView.scrollBy(0, -1);
752 } catch (Exception e) {
753 mAdapterView.setSelection(0);
756 mAdapterView.setSelection(0);
764 * Method that refresh the view data.
766 public void refresh() {
771 * Method that refresh the view data.
773 * @param restore Restore previous position
775 public void refresh(boolean restore) {
776 FileSystemObject fso = null;
777 // Try to restore the previous scroll position
780 if (this.mAdapterView != null && this.mAdapter != null) {
781 int position = this.mAdapterView.getFirstVisiblePosition();
782 fso = this.mAdapter.getItem(position);
784 } catch (Throwable _throw) {/**NON BLOCK**/}
790 * Method that refresh the view data.
792 * @param scrollTo Scroll to object
794 public void refresh(FileSystemObject scrollTo) {
795 //Check that current directory was set
796 if (this.mCurrentDir == null || this.mFiles == null) {
801 changeCurrentDir(this.mCurrentDir, false, true, false, null, scrollTo);
805 * Method that recycles this object
807 public void recycle() {
808 if (this.mAdapter != null) {
809 this.mAdapter.dispose();
814 * Method that change the view mode.
816 * @param newMode The new mode
818 @SuppressWarnings("unchecked")
819 public void changeViewMode(final NavigationLayoutMode newMode) {
820 //Check that it is really necessary change the mode
821 if (this.mCurrentMode != null && this.mCurrentMode.compareTo(newMode) == 0) {
825 // If we should set the listview to response to flinger gesture detection
827 Preferences.getSharedPreferences().getBoolean(
828 FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
829 ((Boolean)FileManagerSettings.
830 SETTINGS_USE_FLINGER.
831 getDefaultValue()).booleanValue());
833 //Creates the new layout
834 AdapterView<ListAdapter> newView = null;
835 int itemResourceId = -1;
836 if (newMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
837 newView = (AdapterView<ListAdapter>)inflate(
838 getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
839 itemResourceId = RESOURCE_MODE_ICONS_ITEM;
841 } else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
842 newView = (AdapterView<ListAdapter>)inflate(
843 getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
844 itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
846 // Set the flinger listener (only when navigate)
847 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
848 if (useFlinger && newView instanceof FlingerListView) {
849 ((FlingerListView)newView).
850 setOnItemFlingerListener(this.mOnItemFlingerListener);
854 } else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
855 newView = (AdapterView<ListAdapter>)inflate(
856 getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
857 itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
859 // Set the flinger listener (only when navigate)
860 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
861 if (useFlinger && newView instanceof FlingerListView) {
862 ((FlingerListView)newView).
863 setOnItemFlingerListener(this.mOnItemFlingerListener);
868 //Get the current adapter and its adapter list
869 List<FileSystemObject> files = new ArrayList<FileSystemObject>(this.mFiles);
870 final AdapterView<ListAdapter> current =
871 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
872 FileSystemObjectAdapter adapter =
873 new FileSystemObjectAdapter(
875 new ArrayList<FileSystemObject>(),
877 this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0);
878 adapter.setOnSelectionChangedListener(this);
880 //Remove current layout
881 if (current != null) {
882 if (current.getAdapter() != null) {
883 //Save selected items before dispose adapter
884 FileSystemObjectAdapter currentAdapter =
885 ((FileSystemObjectAdapter)current.getAdapter());
886 adapter.setSelectedItems(currentAdapter.getSelectedItems());
887 currentAdapter.dispose();
892 adapter.addAll(files);
895 this.mAdapter = adapter;
896 newView.setAdapter(this.mAdapter);
897 newView.setOnItemClickListener(NavigationView.this);
900 this.mAdapterView = newView;
902 this.mCurrentMode = newMode;
904 // Pick mode doesn't implements the onlongclick
905 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
906 this.mAdapterView.setOnItemLongClickListener(this);
908 this.mAdapterView.setOnItemLongClickListener(null);
911 //Save the preference (only in navigation browse mode)
912 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
914 Preferences.savePreference(
915 FileManagerSettings.SETTINGS_LAYOUT_MODE, newMode, true);
916 } catch (Exception ex) {
917 Log.e(TAG, "Save of view mode preference fails", ex); //$NON-NLS-1$
923 * Method that removes a {@link FileSystemObject} from the view
925 * @param fso The file system object
927 public void removeItem(FileSystemObject fso) {
928 // Delete also from internal list
930 int cc = this.mFiles.size()-1;
931 for (int i = cc; i >= 0; i--) {
932 FileSystemObject f = this.mFiles.get(i);
933 if (f != null && f.compareTo(fso) == 0) {
934 this.mFiles.remove(i);
939 this.mAdapter.remove(fso);
943 * Method that removes a file system object from his path from the view
945 * @param path The file system object path
947 public void removeItem(String path) {
948 FileSystemObject fso = this.mAdapter.getItem(path);
950 this.mAdapter.remove(fso);
955 * Method that returns the current directory.
957 * @return String The current directory
959 public String getCurrentDir() {
960 return this.mCurrentDir;
964 * Method that changes the current directory of the view.
966 * @param newDir The new directory location
968 public void changeCurrentDir(final String newDir) {
969 changeCurrentDir(newDir, true, false, false, null, null);
973 * Method that changes the current directory of the view.
975 * @param newDir The new directory location
976 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
978 public void changeCurrentDir(final String newDir, SearchInfoParcelable searchInfo) {
979 changeCurrentDir(newDir, true, false, false, searchInfo, null);
983 * Method that changes the current directory of the view.
985 * @param newDir The new directory location
986 * @param addToHistory Add the directory to history
987 * @param reload Force the reload of the data
988 * @param useCurrent If this method must use the actual data (for back actions)
989 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
990 * @param scrollTo If not null, then listview must scroll to this item
992 private void changeCurrentDir(
993 final String newDir, final boolean addToHistory,
994 final boolean reload, final boolean useCurrent,
995 final SearchInfoParcelable searchInfo, final FileSystemObject scrollTo) {
996 NavigationTask task = new NavigationTask(useCurrent, addToHistory, reload,
997 searchInfo, scrollTo);
998 task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, newDir);
1003 * Method invoked when a execution ends.
1005 * @param files The files obtains from the list
1006 * @param addToHistory If add path to history
1007 * @param isNewHistory If is new history
1008 * @param hasChanged If current directory was changed
1009 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1010 * @param newDir The new directory
1011 * @param scrollTo If not null, then listview must scroll to this item
1014 void onPostExecuteTask(
1015 List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
1016 boolean hasChanged, SearchInfoParcelable searchInfo,
1017 String newDir, final FileSystemObject scrollTo) {
1019 //Check that there is not errors and have some data
1020 if (files == null) {
1024 //Apply user preferences
1025 List<FileSystemObject> sortedFiles =
1026 FileHelper.applyUserPreferences(files, this.mRestrictions, this.mChRooted);
1028 //Remove parent directory if we are in the root of a chrooted environment
1029 if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
1030 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
1036 if (addToHistory && hasChanged && isNewHistory) {
1037 if (this.mOnHistoryListener != null) {
1038 //Communicate the need of a history change
1039 this.mOnHistoryListener.onNewHistory(onSaveState());
1044 loadData(sortedFiles);
1045 this.mFiles = sortedFiles;
1046 if (searchInfo != null) {
1047 searchInfo.setSuccessNavigation(true);
1050 //Change the breadcrumb
1051 if (this.mBreadcrumb != null) {
1052 this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
1055 //If scrollTo is null, the position will be set to 0
1058 //The current directory is now the "newDir"
1059 this.mCurrentDir = newDir;
1060 if (this.mOnDirectoryChangedListener != null) {
1061 FileSystemObject dir = FileHelper.createFileSystemObject(new File(newDir));
1062 this.mOnDirectoryChangedListener.onDirectoryChanged(dir);
1065 //If calling activity is search, then save the search history
1066 if (searchInfo != null) {
1067 this.mOnHistoryListener.onNewHistory(searchInfo);
1070 //End of loading data
1072 NavigationView.this.mBreadcrumb.endLoading();
1073 } catch (Throwable ex) {
1080 * Method that loads the files in the adapter.
1082 * @param files The files to load in the adapter
1085 @SuppressWarnings("unchecked")
1086 private void loadData(final List<FileSystemObject> files) {
1087 //Notify data to adapter view
1088 final AdapterView<ListAdapter> view =
1089 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
1090 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
1091 adapter.setNotifyOnChange(false);
1093 adapter.addAll(files);
1094 adapter.notifyDataSetChanged();
1101 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1102 // Different actions depending on user preference
1104 // Get the adapter and the fso
1105 FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
1106 FileSystemObject fso = adapter.getItem(position);
1108 // Parent directory hasn't actions
1109 if (fso instanceof ParentDirectory) {
1113 // Pick mode doesn't implements the onlongclick
1114 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
1119 return true; //Always consume the event
1123 * Method that opens or navigates to the {@link FileSystemObject}
1125 * @param fso The file system object
1127 public void open(FileSystemObject fso) {
1132 * Method that opens or navigates to the {@link FileSystemObject}
1134 * @param fso The file system object
1135 * @param searchInfo The search info
1137 public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
1138 // If is a folder, then navigate to
1139 if (FileHelper.isDirectory(fso)) {
1140 changeCurrentDir(fso.getFullPath(), searchInfo);
1142 // Open the file with the preferred registered app
1143 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1151 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1153 FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
1154 if (fso instanceof ParentDirectory) {
1155 changeCurrentDir(fso.getParent(), true, false, false, null, null);
1157 } else if (fso instanceof Directory) {
1158 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
1160 } else if (fso instanceof Symlink) {
1161 Symlink symlink = (Symlink)fso;
1162 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
1164 symlink.getLinkRef().getFullPath(), true, false, false, null, null);
1168 // Open the link ref
1169 fso = symlink.getLinkRef();
1172 // Open the file (edit or pick)
1173 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
1174 // Open the file with the preferred registered app
1175 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1177 // Request a file pick selection
1178 if (this.mOnFilePickedListener != null) {
1179 this.mOnFilePickedListener.onFilePicked(fso);
1182 } catch (Throwable ex) {
1183 ExceptionUtil.translateException(getContext(), ex);
1191 public void onRequestRefresh(Object o, boolean clearSelection) {
1192 if (o instanceof FileSystemObject) {
1193 refresh((FileSystemObject)o);
1194 } else if (o == null) {
1197 if (clearSelection) {
1206 public void onRequestRemove(Object o, boolean clearSelection) {
1207 if (o != null && o instanceof FileSystemObject) {
1208 removeItem((FileSystemObject)o);
1210 onRequestRefresh(null, clearSelection);
1212 if (clearSelection) {
1221 public void onNavigateTo(Object o) {
1229 public void onBreadcrumbItemClick(BreadcrumbItem item) {
1230 changeCurrentDir(item.getItemPath(), true, true, false, null, null);
1237 public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
1238 if (this.mOnNavigationSelectionChangedListener != null) {
1239 this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
1244 * Method invoked when a request to show the menu associated
1245 * with an item is started.
1247 * @param item The item for which the request was started
1249 public void onRequestMenu(final FileSystemObject item) {
1250 if (this.mOnNavigationRequestMenuListener != null) {
1251 this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
1259 public void onToggleSelection(FileSystemObject fso) {
1260 if (this.mAdapter != null) {
1261 this.mAdapter.toggleSelection(fso);
1269 public void onDeselectAll() {
1270 if (this.mAdapter != null) {
1271 this.mAdapter.deselectedAll();
1279 public void onSelectAllVisibleItems() {
1280 if (this.mAdapter != null) {
1281 this.mAdapter.selectedAllVisibleItems();
1289 public void onDeselectAllVisibleItems() {
1290 if (this.mAdapter != null) {
1291 this.mAdapter.deselectedAllVisibleItems();
1299 public List<FileSystemObject> onRequestSelectedFiles() {
1300 return this.getSelectedFiles();
1307 public List<FileSystemObject> onRequestCurrentItems() {
1308 return this.getFiles();
1315 public String onRequestCurrentDir() {
1316 return this.mCurrentDir;
1320 * Method that creates a ChRooted environment, protecting the user to break anything
1324 public void createChRooted() {
1325 // If we are in a ChRooted environment, then do nothing
1326 if (this.mChRooted) return;
1327 this.mChRooted = true;
1329 //Change to first storage volume
1330 StorageVolume[] volumes =
1331 StorageHelper.getStorageVolumes(getContext());
1332 if (volumes != null && volumes.length > 0) {
1333 changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1338 * Method that exits from a ChRooted environment
1341 public void exitChRooted() {
1342 // If we aren't in a ChRooted environment, then do nothing
1343 if (!this.mChRooted) return;
1344 this.mChRooted = false;
1351 * Method that ensures that the user don't go outside the ChRooted environment
1353 * @param newDir The new directory to navigate to
1356 private String checkChRootedNavigation(String newDir) {
1357 // If we aren't in ChRooted environment, then there is nothing to check
1358 if (!this.mChRooted) return newDir;
1360 // Check if the path is owned by one of the storage volumes
1361 if (!StorageHelper.isPathInStorageVolume(newDir)) {
1362 StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext());
1363 if (volumes != null && volumes.length > 0) {
1364 return volumes[0].getPath();
1371 * Method that applies the current theme to the activity
1373 public void applyTheme() {
1375 if (getBreadcrumb() != null) {
1376 getBreadcrumb().applyTheme();
1379 //- Redraw the adapter view
1380 Theme theme = ThemeManager.getCurrentTheme(getContext());
1381 theme.setBackgroundDrawable(getContext(), this, "background_drawable"); //$NON-NLS-1$
1382 if (this.mAdapter != null) {
1383 this.mAdapter.notifyThemeChanged();
1385 if (this.mAdapterView instanceof ListView) {
1386 ((ListView)this.mAdapterView).setDivider(
1387 theme.getDrawable(getContext(), "horizontal_divider_drawable")); //$NON-NLS-1$