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);
207 private String mCurrentDir;
208 private NavigationLayoutMode mCurrentMode;
212 List<FileSystemObject> mFiles;
213 private FileSystemObjectAdapter mAdapter;
215 private final Object mSync = new Object();
217 private OnHistoryListener mOnHistoryListener;
218 private OnNavigationSelectionChangedListener mOnNavigationSelectionChangedListener;
219 private OnNavigationRequestMenuListener mOnNavigationRequestMenuListener;
220 private OnFilePickedListener mOnFilePickedListener;
221 private OnDirectoryChangedListener mOnDirectoryChangedListener;
223 private boolean mChRooted;
225 private NAVIGATION_MODE mNavigationMode;
228 private Map<DisplayRestrictions, Object> mRestrictions;
233 Breadcrumb mBreadcrumb;
237 NavigationCustomTitleView mTitle;
241 AdapterView<?> mAdapterView;
243 //The layout for icons mode
244 private static final int RESOURCE_MODE_ICONS_LAYOUT = R.layout.navigation_view_icons;
245 private static final int RESOURCE_MODE_ICONS_ITEM = R.layout.navigation_view_icons_item;
246 //The layout for simple mode
247 private static final int RESOURCE_MODE_SIMPLE_LAYOUT = R.layout.navigation_view_simple;
248 private static final int RESOURCE_MODE_SIMPLE_ITEM = R.layout.navigation_view_simple_item;
249 //The layout for details mode
250 private static final int RESOURCE_MODE_DETAILS_LAYOUT = R.layout.navigation_view_details;
251 private static final int RESOURCE_MODE_DETAILS_ITEM = R.layout.navigation_view_details_item;
253 //The current layout identifier (is shared for all the mode layout)
254 private static final int RESOURCE_CURRENT_LAYOUT = R.id.navigation_view_layout;
257 * Constructor of <code>NavigationView</code>.
259 * @param context The current context
260 * @param attrs The attributes of the XML tag that is inflating the view.
262 public NavigationView(Context context, AttributeSet attrs) {
263 super(context, attrs);
264 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Navigable);
273 * Constructor of <code>NavigationView</code>.
275 * @param context The current context
276 * @param attrs The attributes of the XML tag that is inflating the view.
277 * @param defStyle The default style to apply to this view. If 0, no style
278 * will be applied (beyond what is included in the theme). This may
279 * either be an attribute resource, whose value will be retrieved
280 * from the current theme, or an explicit style resource.
282 public NavigationView(Context context, AttributeSet attrs, int defStyle) {
283 super(context, attrs, defStyle);
284 TypedArray a = context.obtainStyledAttributes(
285 attrs, R.styleable.Navigable, defStyle, 0);
294 * Invoked when the instance need to be saved.
296 * @return NavigationViewInfoParcelable The serialized info
298 public NavigationViewInfoParcelable onSaveState() {
299 //Return the persistent the data
300 NavigationViewInfoParcelable parcel = new NavigationViewInfoParcelable();
301 parcel.setId(this.mId);
302 parcel.setCurrentDir(this.mCurrentDir);
303 parcel.setChRooted(this.mChRooted);
304 parcel.setSelectedFiles(this.mAdapter.getSelectedItems());
305 parcel.setFiles(this.mFiles);
310 * Invoked when the instance need to be restored.
312 * @param info The serialized info
314 public void onRestoreState(NavigationViewInfoParcelable info) {
316 this.mId = info.getId();
317 this.mCurrentDir = info.getCurrentDir();
318 this.mChRooted = info.getChRooted();
319 this.mFiles = info.getFiles();
320 this.mAdapter.setSelectedItems(info.getSelectedFiles());
327 * Method that initializes the view. This method loads all the necessary
328 * information and create an appropriate layout for the view.
330 * @param tarray The type array
332 private void init(TypedArray tarray) {
334 this.mNavigationMode = NAVIGATION_MODE.BROWSABLE;
335 int mode = tarray.getInteger(
336 R.styleable.Navigable_navigation,
337 NAVIGATION_MODE.BROWSABLE.ordinal());
338 if (mode >= 0 && mode < NAVIGATION_MODE.values().length) {
339 this.mNavigationMode = NAVIGATION_MODE.values()[mode];
342 // Initialize default restrictions (no restrictions)
343 this.mRestrictions = new HashMap<DisplayRestrictions, Object>();
345 //Initialize variables
346 this.mFiles = new ArrayList<FileSystemObject>();
348 // Is ChRooted environment?
349 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
350 // Pick mode is always ChRooted
351 this.mChRooted = true;
354 FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
357 //Retrieve the default configuration
358 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
359 SharedPreferences preferences = Preferences.getSharedPreferences();
360 int viewMode = preferences.getInt(
361 FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
362 ((ObjectIdentifier)FileManagerSettings.
363 SETTINGS_LAYOUT_MODE.getDefaultValue()).getId());
364 changeViewMode(NavigationLayoutMode.fromId(viewMode));
366 // Pick mode has always a details layout
367 changeViewMode(NavigationLayoutMode.DETAILS);
372 * Method that returns the display restrictions to apply to this view.
374 * @return Map<DisplayRestrictions, Object> The restrictions to apply
376 public Map<DisplayRestrictions, Object> getRestrictions() {
377 return this.mRestrictions;
381 * Method that sets the display restrictions to apply to this view.
383 * @param mRestrictions The restrictions to apply
385 public void setRestrictions(Map<DisplayRestrictions, Object> mRestrictions) {
386 this.mRestrictions = mRestrictions;
390 * Method that returns the current file list of the navigation view.
392 * @return List<FileSystemObject> The current file list of the navigation view
394 public List<FileSystemObject> getFiles() {
395 if (this.mFiles == null) {
398 return new ArrayList<FileSystemObject>(this.mFiles);
402 * Method that returns the current file list of the navigation view.
404 * @return List<FileSystemObject> The current file list of the navigation view
406 public List<FileSystemObject> getSelectedFiles() {
407 if (this.mAdapter != null && this.mAdapter.getSelectedItems() != null) {
408 return new ArrayList<FileSystemObject>(this.mAdapter.getSelectedItems());
414 * Method that returns the custom title fragment associated with this navigation view.
416 * @return NavigationCustomTitleView The custom title view fragment
418 public NavigationCustomTitleView getCustomTitle() {
423 * Method that associates the custom title fragment with this navigation view.
425 * @param title The custom title view fragment
427 public void setCustomTitle(NavigationCustomTitleView title) {
432 * Method that returns the breadcrumb associated with this navigation view.
434 * @return Breadcrumb The breadcrumb view fragment
436 public Breadcrumb getBreadcrumb() {
437 return this.mBreadcrumb;
441 * Method that associates the breadcrumb with this navigation view.
443 * @param breadcrumb The breadcrumb view fragment
445 public void setBreadcrumb(Breadcrumb breadcrumb) {
446 this.mBreadcrumb = breadcrumb;
447 this.mBreadcrumb.addBreadcrumbListener(this);
451 * Method that sets the listener for communicate history changes.
453 * @param onHistoryListener The listener for communicate history changes
455 public void setOnHistoryListener(OnHistoryListener onHistoryListener) {
456 this.mOnHistoryListener = onHistoryListener;
460 * Method that sets the listener which communicates selection changes.
462 * @param onNavigationSelectionChangedListener The listener reference
464 public void setOnNavigationSelectionChangedListener(
465 OnNavigationSelectionChangedListener onNavigationSelectionChangedListener) {
466 this.mOnNavigationSelectionChangedListener = onNavigationSelectionChangedListener;
470 * Method that sets the listener for menu item requests.
472 * @param onNavigationRequestMenuListener The listener reference
474 public void setOnNavigationOnRequestMenuListener(
475 OnNavigationRequestMenuListener onNavigationRequestMenuListener) {
476 this.mOnNavigationRequestMenuListener = onNavigationRequestMenuListener;
480 * @return the mOnFilePickedListener
482 public OnFilePickedListener getOnFilePickedListener() {
483 return this.mOnFilePickedListener;
487 * Method that sets the listener for picked items
489 * @param onFilePickedListener The listener reference
491 public void setOnFilePickedListener(OnFilePickedListener onFilePickedListener) {
492 this.mOnFilePickedListener = onFilePickedListener;
496 * Method that sets the listener for directory changes
498 * @param onDirectoryChangedListener The listener reference
500 public void setOnDirectoryChangedListener(
501 OnDirectoryChangedListener onDirectoryChangedListener) {
502 this.mOnDirectoryChangedListener = onDirectoryChangedListener;
506 * Method that sets if the view should use flinger gesture detection.
508 * @param useFlinger If the view should use flinger gesture detection
510 public void setUseFlinger(boolean useFlinger) {
511 if (this.mCurrentMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
515 // Set the flinger listener (only when navigate)
516 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
517 if (this.mAdapterView instanceof FlingerListView) {
519 ((FlingerListView)this.mAdapterView).
520 setOnItemFlingerListener(this.mOnItemFlingerListener);
522 ((FlingerListView)this.mAdapterView).setOnItemFlingerListener(null);
529 * Method that forces the view to scroll to the file system object passed.
531 * @param fso The file system object
533 public void scrollTo(FileSystemObject fso) {
536 int position = this.mAdapter.getPosition(fso);
537 this.mAdapterView.setSelection(position);
538 } catch (Exception e) {
539 this.mAdapterView.setSelection(0);
542 this.mAdapterView.setSelection(0);
547 * Method that refresh the view data.
549 public void refresh() {
554 * Method that refresh the view data.
556 * @param restore Restore previous position
558 public void refresh(boolean restore) {
559 FileSystemObject fso = null;
560 // Try to restore the previous scroll position
563 if (this.mAdapterView != null && this.mAdapter != null) {
564 int position = this.mAdapterView.getFirstVisiblePosition();
565 fso = this.mAdapter.getItem(position);
567 } catch (Throwable _throw) {/**NON BLOCK**/}
573 * Method that refresh the view data.
575 * @param scrollTo Scroll to object
577 public void refresh(FileSystemObject scrollTo) {
578 //Check that current directory was set
579 if (this.mCurrentDir == null || this.mFiles == null) {
584 changeCurrentDir(this.mCurrentDir, false, true, false, null, scrollTo);
588 * Method that change the view mode.
590 * @param newMode The new mode
592 @SuppressWarnings({ "unchecked", "null" })
593 public void changeViewMode(final NavigationLayoutMode newMode) {
594 synchronized (this.mSync) {
595 //Check that it is really necessary change the mode
596 if (this.mCurrentMode != null && this.mCurrentMode.compareTo(newMode) == 0) {
600 // If we should set the listview to response to flinger gesture detection
602 Preferences.getSharedPreferences().getBoolean(
603 FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
604 ((Boolean)FileManagerSettings.
605 SETTINGS_USE_FLINGER.
606 getDefaultValue()).booleanValue());
608 //Creates the new layout
609 AdapterView<ListAdapter> newView = null;
610 int itemResourceId = -1;
611 if (newMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
612 newView = (AdapterView<ListAdapter>)inflate(
613 getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
614 itemResourceId = RESOURCE_MODE_ICONS_ITEM;
616 } else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
617 newView = (AdapterView<ListAdapter>)inflate(
618 getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
619 itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
621 // Set the flinger listener (only when navigate)
622 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
623 if (useFlinger && newView instanceof FlingerListView) {
624 ((FlingerListView)newView).
625 setOnItemFlingerListener(this.mOnItemFlingerListener);
629 } else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
630 newView = (AdapterView<ListAdapter>)inflate(
631 getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
632 itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
634 // Set the flinger listener (only when navigate)
635 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
636 if (useFlinger && newView instanceof FlingerListView) {
637 ((FlingerListView)newView).
638 setOnItemFlingerListener(this.mOnItemFlingerListener);
643 //Get the current adapter and its adapter list
644 List<FileSystemObject> files = new ArrayList<FileSystemObject>(this.mFiles);
645 final AdapterView<ListAdapter> current =
646 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
647 FileSystemObjectAdapter adapter =
648 new FileSystemObjectAdapter(
650 new ArrayList<FileSystemObject>(),
652 this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0);
653 adapter.setOnSelectionChangedListener(this);
655 //Remove current layout
656 if (current != null) {
657 if (current.getAdapter() != null) {
658 //Save selected items before dispose adapter
659 FileSystemObjectAdapter currentAdapter =
660 ((FileSystemObjectAdapter)current.getAdapter());
661 adapter.setSelectedItems(currentAdapter.getSelectedItems());
662 currentAdapter.dispose();
667 adapter.addAll(files);
668 adapter.notifyDataSetChanged();
671 this.mAdapter = adapter;
672 newView.setAdapter(this.mAdapter);
673 newView.setOnItemClickListener(NavigationView.this);
676 this.mAdapterView = newView;
678 this.mCurrentMode = newMode;
680 // Pick mode doesn't implements the onlongclick
681 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
682 this.mAdapterView.setOnItemLongClickListener(this);
684 this.mAdapterView.setOnItemLongClickListener(null);
687 //Save the preference (only in navigation browse mode)
688 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
690 Preferences.savePreference(
691 FileManagerSettings.SETTINGS_LAYOUT_MODE, newMode, true);
692 } catch (Exception ex) {
693 Log.e(TAG, "Save of view mode preference fails", ex); //$NON-NLS-1$
700 * Method that removes a {@link FileSystemObject} from the view
702 * @param fso The file system object
704 public void removeItem(FileSystemObject fso) {
705 this.mAdapter.remove(fso);
706 // Delete also from internal list
708 int cc = this.mFiles.size()-1;
709 for (int i = cc; i >= 0; i--) {
710 FileSystemObject f = this.mFiles.get(i);
711 if (f != null && f.compareTo(fso) == 0) {
712 this.mFiles.remove(i);
717 this.mAdapter.notifyDataSetChanged();
721 * Method that removes a file system object from his path from the view
723 * @param path The file system object path
725 public void removeItem(String path) {
726 FileSystemObject fso = this.mAdapter.getItem(path);
728 this.mAdapter.remove(fso);
729 this.mAdapter.notifyDataSetChanged();
734 * Method that returns the current directory.
736 * @return String The current directory
738 public String getCurrentDir() {
739 return this.mCurrentDir;
743 * Method that changes the current directory of the view.
745 * @param newDir The new directory location
747 public void changeCurrentDir(final String newDir) {
748 changeCurrentDir(newDir, true, false, false, null, null);
752 * Method that changes the current directory of the view.
754 * @param newDir The new directory location
755 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
757 public void changeCurrentDir(final String newDir, SearchInfoParcelable searchInfo) {
758 changeCurrentDir(newDir, true, false, false, searchInfo, null);
762 * Method that changes the current directory of the view.
764 * @param newDir The new directory location
765 * @param addToHistory Add the directory to history
766 * @param reload Force the reload of the data
767 * @param useCurrent If this method must use the actual data (for back actions)
768 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
769 * @param scrollTo If not null, then listview must scroll to this item
771 private void changeCurrentDir(
772 final String newDir, final boolean addToHistory,
773 final boolean reload, final boolean useCurrent,
774 final SearchInfoParcelable searchInfo, final FileSystemObject scrollTo) {
776 // Check navigation security (don't allow to go outside the ChRooted environment if one
778 final String fNewDir = checkChRootedNavigation(newDir);
780 synchronized (this.mSync) {
781 //Check that it is really necessary change the directory
782 if (!reload && this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0) {
786 final boolean hasChanged =
787 !(this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0);
788 final boolean isNewHistory = (this.mCurrentDir != null);
790 //Execute the listing in a background process
791 AsyncTask<String, Integer, List<FileSystemObject>> task =
792 new AsyncTask<String, Integer, List<FileSystemObject>>() {
797 protected List<FileSystemObject> doInBackground(String... params) {
799 //Reset the custom title view and returns to breadcrumb
800 if (NavigationView.this.mTitle != null) {
801 NavigationView.this.mTitle.post(new Runnable() {
805 NavigationView.this.mTitle.restoreView();
806 } catch (Exception e) {
814 //Start of loading data
815 if (NavigationView.this.mBreadcrumb != null) {
817 NavigationView.this.mBreadcrumb.startLoading();
818 } catch (Throwable ex) {
823 //Get the files, resolve links and apply configuration
824 //(sort, hidden, ...)
825 List<FileSystemObject> files = NavigationView.this.mFiles;
827 files = CommandHelper.listFiles(getContext(), fNewDir, null);
830 } catch (final ConsoleAllocException e) {
831 //Show exception and exists
832 NavigationView.this.post(new Runnable() {
835 Context ctx = getContext();
836 Log.e(TAG, ctx.getString(
837 R.string.msgs_cant_create_console), e);
838 DialogHelper.showToast(ctx,
839 R.string.msgs_cant_create_console,
841 ((Activity)ctx).finish();
846 } catch (Exception ex) {
847 //End of loading data
848 if (NavigationView.this.mBreadcrumb != null) {
850 NavigationView.this.mBreadcrumb.endLoading();
851 } catch (Throwable ex2) {
856 //Capture exception (attach task, and use listener to do the anim)
857 ExceptionUtil.attachAsyncTask(
859 new AsyncTask<Object, Integer, Boolean>() {
860 private List<FileSystemObject> mTaskFiles = null;
863 "unchecked", "unqualified-field-access"
865 protected Boolean doInBackground(Object... taskParams) {
866 mTaskFiles = (List<FileSystemObject>)taskParams[0];
871 @SuppressWarnings("unqualified-field-access")
872 protected void onPostExecute(Boolean result) {
873 if (!result.booleanValue()) {
877 mTaskFiles, addToHistory,
878 isNewHistory, hasChanged,
879 searchInfo, fNewDir, scrollTo);
882 final OnRelaunchCommandResult exListener =
883 new OnRelaunchCommandResult() {
885 public void onSuccess() {
890 public void onFailed(Throwable cause) {
895 public void onCancelled() {
900 ExceptionUtil.translateException(
901 getContext(), ex, false, true, exListener);
910 protected void onPreExecute() {
921 protected void onPostExecute(List<FileSystemObject> files) {
924 files, addToHistory, isNewHistory,
925 hasChanged, searchInfo, fNewDir, scrollTo);
933 * Method that performs a fade animation.
935 * @param out Fade out (true); Fade in (false)
937 void fadeEfect(final boolean out) {
938 Activity activity = (Activity)getContext();
939 activity.runOnUiThread(new Runnable() {
942 Animation fadeAnim = out ?
943 new AlphaAnimation(1, 0) :
944 new AlphaAnimation(0, 1);
945 fadeAnim.setDuration(50L);
946 fadeAnim.setFillAfter(true);
947 fadeAnim.setInterpolator(new AccelerateInterpolator());
948 NavigationView.this.startAnimation(fadeAnim);
953 task.execute(fNewDir);
959 * Method invoked when a execution ends.
961 * @param files The files obtains from the list
962 * @param addToHistory If add path to history
963 * @param isNewHistory If is new history
964 * @param hasChanged If current directory was changed
965 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
966 * @param newDir The new directory
967 * @param scrollTo If not null, then listview must scroll to this item
970 void onPostExecuteTask(
971 List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
972 boolean hasChanged, SearchInfoParcelable searchInfo,
973 String newDir, final FileSystemObject scrollTo) {
975 //Check that there is not errors and have some data
980 //Apply user preferences
981 List<FileSystemObject> sortedFiles =
982 FileHelper.applyUserPreferences(files, this.mRestrictions, this.mChRooted);
984 //Remove parent directory if we are in the root of a chrooted environment
985 if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
986 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
992 loadData(sortedFiles);
993 this.mFiles = sortedFiles;
994 if (searchInfo != null) {
995 searchInfo.setSuccessNavigation(true);
999 if (addToHistory && hasChanged && isNewHistory) {
1000 if (this.mOnHistoryListener != null) {
1001 //Communicate the need of a history change
1002 this.mOnHistoryListener.onNewHistory(onSaveState());
1006 //Change the breadcrumb
1007 if (this.mBreadcrumb != null) {
1008 this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
1012 if (scrollTo != null) {
1016 //The current directory is now the "newDir"
1017 this.mCurrentDir = newDir;
1018 if (this.mOnDirectoryChangedListener != null) {
1019 FileSystemObject dir = FileHelper.createFileSystemObject(new File(newDir));
1020 this.mOnDirectoryChangedListener.onDirectoryChanged(dir);
1023 //If calling activity is search, then save the search history
1024 if (searchInfo != null) {
1025 this.mOnHistoryListener.onNewHistory(searchInfo);
1028 //End of loading data
1030 NavigationView.this.mBreadcrumb.endLoading();
1031 } catch (Throwable ex) {
1038 * Method that loads the files in the adapter.
1040 * @param files The files to load in the adapter
1043 @SuppressWarnings("unchecked")
1044 private void loadData(final List<FileSystemObject> files) {
1045 //Notify data to adapter view
1046 final AdapterView<ListAdapter> view =
1047 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
1048 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
1050 adapter.addAll(files);
1051 adapter.notifyDataSetChanged();
1052 view.setSelection(0);
1059 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1060 // Different actions depending on user preference
1062 // Get the adapter and the fso
1063 FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
1064 FileSystemObject fso = adapter.getItem(position);
1066 // Parent directory hasn't actions
1067 if (fso instanceof ParentDirectory) {
1071 // Pick mode doesn't implements the onlongclick
1072 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
1077 return true; //Always consume the event
1081 * Method that opens or navigates to the {@link FileSystemObject}
1083 * @param fso The file system object
1085 public void open(FileSystemObject fso) {
1090 * Method that opens or navigates to the {@link FileSystemObject}
1092 * @param fso The file system object
1093 * @param searchInfo The search info
1095 public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
1096 // If is a folder, then navigate to
1097 if (FileHelper.isDirectory(fso)) {
1098 changeCurrentDir(fso.getFullPath(), searchInfo);
1100 // Open the file with the preferred registered app
1101 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1109 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1111 FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
1112 if (fso instanceof ParentDirectory) {
1113 changeCurrentDir(fso.getParent(), true, false, false, null, null);
1115 } else if (fso instanceof Directory) {
1116 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
1118 } else if (fso instanceof Symlink) {
1119 Symlink symlink = (Symlink)fso;
1120 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
1122 symlink.getLinkRef().getFullPath(), true, false, false, null, null);
1126 // Open the link ref
1127 fso = symlink.getLinkRef();
1130 // Open the file (edit or pick)
1131 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
1132 // Open the file with the preferred registered app
1133 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1135 // Request a file pick selection
1136 if (this.mOnFilePickedListener != null) {
1137 this.mOnFilePickedListener.onFilePicked(fso);
1140 } catch (Throwable ex) {
1141 ExceptionUtil.translateException(getContext(), ex);
1149 public void onRequestRefresh(Object o, boolean clearSelection) {
1150 if (o instanceof FileSystemObject) {
1151 refresh((FileSystemObject)o);
1152 } else if (o == null) {
1155 if (clearSelection) {
1164 public void onRequestRemove(Object o, boolean clearSelection) {
1165 if (o != null && o instanceof FileSystemObject) {
1166 removeItem((FileSystemObject)o);
1168 onRequestRefresh(null, clearSelection);
1170 if (clearSelection) {
1179 public void onNavigateTo(Object o) {
1187 public void onBreadcrumbItemClick(BreadcrumbItem item) {
1188 changeCurrentDir(item.getItemPath(), true, true, false, null, null);
1195 public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
1196 if (this.mOnNavigationSelectionChangedListener != null) {
1197 this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
1202 * Method invoked when a request to show the menu associated
1203 * with an item is started.
1205 * @param item The item for which the request was started
1207 public void onRequestMenu(final FileSystemObject item) {
1208 if (this.mOnNavigationRequestMenuListener != null) {
1209 this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
1217 public void onToggleSelection(FileSystemObject fso) {
1218 if (this.mAdapter != null) {
1219 this.mAdapter.toggleSelection(fso);
1227 public void onDeselectAll() {
1228 if (this.mAdapter != null) {
1229 this.mAdapter.deselectedAll();
1237 public void onSelectAllVisibleItems() {
1238 if (this.mAdapter != null) {
1239 this.mAdapter.selectedAllVisibleItems();
1247 public void onDeselectAllVisibleItems() {
1248 if (this.mAdapter != null) {
1249 this.mAdapter.deselectedAllVisibleItems();
1257 public List<FileSystemObject> onRequestSelectedFiles() {
1258 return this.getSelectedFiles();
1265 public List<FileSystemObject> onRequestCurrentItems() {
1266 return this.getFiles();
1273 public String onRequestCurrentDir() {
1274 return this.mCurrentDir;
1278 * Method that creates a ChRooted environment, protecting the user to break anything
1282 public void createChRooted() {
1283 // If we are in a ChRooted environment, then do nothing
1284 if (this.mChRooted) return;
1285 this.mChRooted = true;
1287 //Change to first storage volume
1288 StorageVolume[] volumes =
1289 StorageHelper.getStorageVolumes(getContext());
1290 if (volumes != null && volumes.length > 0) {
1291 changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1296 * Method that exits from a ChRooted environment
1299 public void exitChRooted() {
1300 // If we aren't in a ChRooted environment, then do nothing
1301 if (!this.mChRooted) return;
1302 this.mChRooted = false;
1309 * Method that ensures that the user don't go outside the ChRooted environment
1311 * @param newDir The new directory to navigate to
1314 private String checkChRootedNavigation(String newDir) {
1315 // If we aren't in ChRooted environment, then there is nothing to check
1316 if (!this.mChRooted) return newDir;
1318 // Check if the path is owned by one of the storage volumes
1319 if (!StorageHelper.isPathInStorageVolume(newDir)) {
1320 StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext());
1321 if (volumes != null && volumes.length > 0) {
1322 return volumes[0].getPath();
1329 * Method that applies the current theme to the activity
1331 public void applyTheme() {
1333 if (getBreadcrumb() != null) {
1334 getBreadcrumb().applyTheme();
1337 //- Redraw the adapter view
1338 Theme theme = ThemeManager.getCurrentTheme(getContext());
1339 theme.setBackgroundDrawable(getContext(), this, "background_drawable"); //$NON-NLS-1$
1340 if (this.mAdapter != null) {
1341 this.mAdapter.notifyThemeChanged();
1343 if (this.mAdapterView instanceof ListView) {
1344 ((ListView)this.mAdapterView).setDivider(
1345 theme.getDrawable(getContext(), "horizontal_divider_drawable")); //$NON-NLS-1$