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 boolean mChangingDir;
216 private final Object mSync = new Object();
218 private OnHistoryListener mOnHistoryListener;
219 private OnNavigationSelectionChangedListener mOnNavigationSelectionChangedListener;
220 private OnNavigationRequestMenuListener mOnNavigationRequestMenuListener;
221 private OnFilePickedListener mOnFilePickedListener;
222 private OnDirectoryChangedListener mOnDirectoryChangedListener;
224 private boolean mChRooted;
226 private NAVIGATION_MODE mNavigationMode;
229 private Map<DisplayRestrictions, Object> mRestrictions;
234 Breadcrumb mBreadcrumb;
238 NavigationCustomTitleView mTitle;
242 AdapterView<?> mAdapterView;
244 //The layout for icons mode
245 private static final int RESOURCE_MODE_ICONS_LAYOUT = R.layout.navigation_view_icons;
246 private static final int RESOURCE_MODE_ICONS_ITEM = R.layout.navigation_view_icons_item;
247 //The layout for simple mode
248 private static final int RESOURCE_MODE_SIMPLE_LAYOUT = R.layout.navigation_view_simple;
249 private static final int RESOURCE_MODE_SIMPLE_ITEM = R.layout.navigation_view_simple_item;
250 //The layout for details mode
251 private static final int RESOURCE_MODE_DETAILS_LAYOUT = R.layout.navigation_view_details;
252 private static final int RESOURCE_MODE_DETAILS_ITEM = R.layout.navigation_view_details_item;
254 //The current layout identifier (is shared for all the mode layout)
255 private static final int RESOURCE_CURRENT_LAYOUT = R.id.navigation_view_layout;
258 * Constructor of <code>NavigationView</code>.
260 * @param context The current context
261 * @param attrs The attributes of the XML tag that is inflating the view.
263 public NavigationView(Context context, AttributeSet attrs) {
264 super(context, attrs);
265 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Navigable);
274 * Constructor of <code>NavigationView</code>.
276 * @param context The current context
277 * @param attrs The attributes of the XML tag that is inflating the view.
278 * @param defStyle The default style to apply to this view. If 0, no style
279 * will be applied (beyond what is included in the theme). This may
280 * either be an attribute resource, whose value will be retrieved
281 * from the current theme, or an explicit style resource.
283 public NavigationView(Context context, AttributeSet attrs, int defStyle) {
284 super(context, attrs, defStyle);
285 TypedArray a = context.obtainStyledAttributes(
286 attrs, R.styleable.Navigable, defStyle, 0);
295 * Invoked when the instance need to be saved.
297 * @return NavigationViewInfoParcelable The serialized info
299 public NavigationViewInfoParcelable onSaveState() {
300 //Return the persistent the data
301 NavigationViewInfoParcelable parcel = new NavigationViewInfoParcelable();
302 parcel.setId(this.mId);
303 parcel.setCurrentDir(this.mCurrentDir);
304 parcel.setChRooted(this.mChRooted);
305 parcel.setSelectedFiles(this.mAdapter.getSelectedItems());
306 parcel.setFiles(this.mFiles);
311 * Invoked when the instance need to be restored.
313 * @param info The serialized info
314 * @return boolean If can restore
316 public boolean onRestoreState(NavigationViewInfoParcelable info) {
317 synchronized (mSync) {
324 this.mId = info.getId();
325 this.mCurrentDir = info.getCurrentDir();
326 this.mChRooted = info.getChRooted();
327 this.mFiles = info.getFiles();
328 this.mAdapter.setSelectedItems(info.getSelectedFiles());
336 * Method that initializes the view. This method loads all the necessary
337 * information and create an appropriate layout for the view.
339 * @param tarray The type array
341 private void init(TypedArray tarray) {
343 this.mNavigationMode = NAVIGATION_MODE.BROWSABLE;
344 int mode = tarray.getInteger(
345 R.styleable.Navigable_navigation,
346 NAVIGATION_MODE.BROWSABLE.ordinal());
347 if (mode >= 0 && mode < NAVIGATION_MODE.values().length) {
348 this.mNavigationMode = NAVIGATION_MODE.values()[mode];
351 // Initialize default restrictions (no restrictions)
352 this.mRestrictions = new HashMap<DisplayRestrictions, Object>();
354 //Initialize variables
355 this.mFiles = new ArrayList<FileSystemObject>();
357 // Is ChRooted environment?
358 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
359 // Pick mode is always ChRooted
360 this.mChRooted = true;
363 FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
366 //Retrieve the default configuration
367 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
368 SharedPreferences preferences = Preferences.getSharedPreferences();
369 int viewMode = preferences.getInt(
370 FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
371 ((ObjectIdentifier)FileManagerSettings.
372 SETTINGS_LAYOUT_MODE.getDefaultValue()).getId());
373 changeViewMode(NavigationLayoutMode.fromId(viewMode));
375 // Pick mode has always a details layout
376 changeViewMode(NavigationLayoutMode.DETAILS);
381 * Method that returns the display restrictions to apply to this view.
383 * @return Map<DisplayRestrictions, Object> The restrictions to apply
385 public Map<DisplayRestrictions, Object> getRestrictions() {
386 return this.mRestrictions;
390 * Method that sets the display restrictions to apply to this view.
392 * @param mRestrictions The restrictions to apply
394 public void setRestrictions(Map<DisplayRestrictions, Object> mRestrictions) {
395 this.mRestrictions = mRestrictions;
399 * Method that returns the current file list of the navigation view.
401 * @return List<FileSystemObject> The current file list of the navigation view
403 public List<FileSystemObject> getFiles() {
404 if (this.mFiles == null) {
407 return new ArrayList<FileSystemObject>(this.mFiles);
411 * Method that returns the current file list of the navigation view.
413 * @return List<FileSystemObject> The current file list of the navigation view
415 public List<FileSystemObject> getSelectedFiles() {
416 if (this.mAdapter != null && this.mAdapter.getSelectedItems() != null) {
417 return new ArrayList<FileSystemObject>(this.mAdapter.getSelectedItems());
423 * Method that returns the custom title fragment associated with this navigation view.
425 * @return NavigationCustomTitleView The custom title view fragment
427 public NavigationCustomTitleView getCustomTitle() {
432 * Method that associates the custom title fragment with this navigation view.
434 * @param title The custom title view fragment
436 public void setCustomTitle(NavigationCustomTitleView title) {
441 * Method that returns the breadcrumb associated with this navigation view.
443 * @return Breadcrumb The breadcrumb view fragment
445 public Breadcrumb getBreadcrumb() {
446 return this.mBreadcrumb;
450 * Method that associates the breadcrumb with this navigation view.
452 * @param breadcrumb The breadcrumb view fragment
454 public void setBreadcrumb(Breadcrumb breadcrumb) {
455 this.mBreadcrumb = breadcrumb;
456 this.mBreadcrumb.addBreadcrumbListener(this);
460 * Method that sets the listener for communicate history changes.
462 * @param onHistoryListener The listener for communicate history changes
464 public void setOnHistoryListener(OnHistoryListener onHistoryListener) {
465 this.mOnHistoryListener = onHistoryListener;
469 * Method that sets the listener which communicates selection changes.
471 * @param onNavigationSelectionChangedListener The listener reference
473 public void setOnNavigationSelectionChangedListener(
474 OnNavigationSelectionChangedListener onNavigationSelectionChangedListener) {
475 this.mOnNavigationSelectionChangedListener = onNavigationSelectionChangedListener;
479 * Method that sets the listener for menu item requests.
481 * @param onNavigationRequestMenuListener The listener reference
483 public void setOnNavigationOnRequestMenuListener(
484 OnNavigationRequestMenuListener onNavigationRequestMenuListener) {
485 this.mOnNavigationRequestMenuListener = onNavigationRequestMenuListener;
489 * @return the mOnFilePickedListener
491 public OnFilePickedListener getOnFilePickedListener() {
492 return this.mOnFilePickedListener;
496 * Method that sets the listener for picked items
498 * @param onFilePickedListener The listener reference
500 public void setOnFilePickedListener(OnFilePickedListener onFilePickedListener) {
501 this.mOnFilePickedListener = onFilePickedListener;
505 * Method that sets the listener for directory changes
507 * @param onDirectoryChangedListener The listener reference
509 public void setOnDirectoryChangedListener(
510 OnDirectoryChangedListener onDirectoryChangedListener) {
511 this.mOnDirectoryChangedListener = onDirectoryChangedListener;
515 * Method that sets if the view should use flinger gesture detection.
517 * @param useFlinger If the view should use flinger gesture detection
519 public void setUseFlinger(boolean useFlinger) {
520 if (this.mCurrentMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
524 // Set the flinger listener (only when navigate)
525 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
526 if (this.mAdapterView instanceof FlingerListView) {
528 ((FlingerListView)this.mAdapterView).
529 setOnItemFlingerListener(this.mOnItemFlingerListener);
531 ((FlingerListView)this.mAdapterView).setOnItemFlingerListener(null);
538 * Method that forces the view to scroll to the file system object passed.
540 * @param fso The file system object
542 public void scrollTo(FileSystemObject fso) {
545 int position = this.mAdapter.getPosition(fso);
546 this.mAdapterView.setSelection(position);
547 } catch (Exception e) {
548 this.mAdapterView.setSelection(0);
551 this.mAdapterView.setSelection(0);
556 * Method that refresh the view data.
558 public void refresh() {
563 * Method that refresh the view data.
565 * @param restore Restore previous position
567 public void refresh(boolean restore) {
568 FileSystemObject fso = null;
569 // Try to restore the previous scroll position
572 if (this.mAdapterView != null && this.mAdapter != null) {
573 int position = this.mAdapterView.getFirstVisiblePosition();
574 fso = this.mAdapter.getItem(position);
576 } catch (Throwable _throw) {/**NON BLOCK**/}
582 * Method that refresh the view data.
584 * @param scrollTo Scroll to object
586 public void refresh(FileSystemObject scrollTo) {
587 //Check that current directory was set
588 if (this.mCurrentDir == null || this.mFiles == null) {
593 changeCurrentDir(this.mCurrentDir, false, true, false, null, scrollTo);
597 * Method that recycles this object
599 public void recycle() {
600 if (this.mAdapter != null) {
601 this.mAdapter.dispose();
606 * Method that change the view mode.
608 * @param newMode The new mode
610 @SuppressWarnings("unchecked")
611 public void changeViewMode(final NavigationLayoutMode newMode) {
612 synchronized (this.mSync) {
613 //Check that it is really necessary change the mode
614 if (this.mCurrentMode != null && this.mCurrentMode.compareTo(newMode) == 0) {
618 // If we should set the listview to response to flinger gesture detection
620 Preferences.getSharedPreferences().getBoolean(
621 FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
622 ((Boolean)FileManagerSettings.
623 SETTINGS_USE_FLINGER.
624 getDefaultValue()).booleanValue());
626 //Creates the new layout
627 AdapterView<ListAdapter> newView = null;
628 int itemResourceId = -1;
629 if (newMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
630 newView = (AdapterView<ListAdapter>)inflate(
631 getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
632 itemResourceId = RESOURCE_MODE_ICONS_ITEM;
634 } else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
635 newView = (AdapterView<ListAdapter>)inflate(
636 getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
637 itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
639 // Set the flinger listener (only when navigate)
640 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
641 if (useFlinger && newView instanceof FlingerListView) {
642 ((FlingerListView)newView).
643 setOnItemFlingerListener(this.mOnItemFlingerListener);
647 } else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
648 newView = (AdapterView<ListAdapter>)inflate(
649 getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
650 itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
652 // Set the flinger listener (only when navigate)
653 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
654 if (useFlinger && newView instanceof FlingerListView) {
655 ((FlingerListView)newView).
656 setOnItemFlingerListener(this.mOnItemFlingerListener);
661 //Get the current adapter and its adapter list
662 List<FileSystemObject> files = new ArrayList<FileSystemObject>(this.mFiles);
663 final AdapterView<ListAdapter> current =
664 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
665 FileSystemObjectAdapter adapter =
666 new FileSystemObjectAdapter(
668 new ArrayList<FileSystemObject>(),
670 this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0);
671 adapter.setOnSelectionChangedListener(this);
673 //Remove current layout
674 if (current != null) {
675 if (current.getAdapter() != null) {
676 //Save selected items before dispose adapter
677 FileSystemObjectAdapter currentAdapter =
678 ((FileSystemObjectAdapter)current.getAdapter());
679 adapter.setSelectedItems(currentAdapter.getSelectedItems());
680 currentAdapter.dispose();
685 adapter.addAll(files);
688 this.mAdapter = adapter;
689 newView.setAdapter(this.mAdapter);
690 newView.setOnItemClickListener(NavigationView.this);
693 this.mAdapterView = newView;
695 this.mCurrentMode = newMode;
697 // Pick mode doesn't implements the onlongclick
698 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
699 this.mAdapterView.setOnItemLongClickListener(this);
701 this.mAdapterView.setOnItemLongClickListener(null);
704 //Save the preference (only in navigation browse mode)
705 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
707 Preferences.savePreference(
708 FileManagerSettings.SETTINGS_LAYOUT_MODE, newMode, true);
709 } catch (Exception ex) {
710 Log.e(TAG, "Save of view mode preference fails", ex); //$NON-NLS-1$
717 * Method that removes a {@link FileSystemObject} from the view
719 * @param fso The file system object
721 public void removeItem(FileSystemObject fso) {
722 // Delete also from internal list
724 int cc = this.mFiles.size()-1;
725 for (int i = cc; i >= 0; i--) {
726 FileSystemObject f = this.mFiles.get(i);
727 if (f != null && f.compareTo(fso) == 0) {
728 this.mFiles.remove(i);
733 this.mAdapter.remove(fso);
737 * Method that removes a file system object from his path from the view
739 * @param path The file system object path
741 public void removeItem(String path) {
742 FileSystemObject fso = this.mAdapter.getItem(path);
744 this.mAdapter.remove(fso);
749 * Method that returns the current directory.
751 * @return String The current directory
753 public String getCurrentDir() {
754 return this.mCurrentDir;
758 * Method that changes the current directory of the view.
760 * @param newDir The new directory location
762 public void changeCurrentDir(final String newDir) {
763 changeCurrentDir(newDir, true, false, false, null, null);
767 * Method that changes the current directory of the view.
769 * @param newDir The new directory location
770 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
772 public void changeCurrentDir(final String newDir, SearchInfoParcelable searchInfo) {
773 changeCurrentDir(newDir, true, false, false, searchInfo, null);
777 * Method that changes the current directory of the view.
779 * @param newDir The new directory location
780 * @param addToHistory Add the directory to history
781 * @param reload Force the reload of the data
782 * @param useCurrent If this method must use the actual data (for back actions)
783 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
784 * @param scrollTo If not null, then listview must scroll to this item
786 private void changeCurrentDir(
787 final String newDir, final boolean addToHistory,
788 final boolean reload, final boolean useCurrent,
789 final SearchInfoParcelable searchInfo, final FileSystemObject scrollTo) {
791 // Check navigation security (don't allow to go outside the ChRooted environment if one
793 final String fNewDir = checkChRootedNavigation(newDir);
795 // Wait to finalization
796 synchronized (this.mSync) {
800 } catch (InterruptedException iex) {
807 //Check that it is really necessary change the directory
808 if (!reload && this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0) {
809 synchronized (this.mSync) {
810 mChangingDir = false;
816 final boolean hasChanged =
817 !(this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0);
818 final boolean isNewHistory = (this.mCurrentDir != null);
820 //Execute the listing in a background process
821 AsyncTask<String, Integer, List<FileSystemObject>> task =
822 new AsyncTask<String, Integer, List<FileSystemObject>>() {
827 protected List<FileSystemObject> doInBackground(String... params) {
829 //Reset the custom title view and returns to breadcrumb
830 if (NavigationView.this.mTitle != null) {
831 NavigationView.this.mTitle.post(new Runnable() {
835 NavigationView.this.mTitle.restoreView();
836 } catch (Exception e) {
844 //Start of loading data
845 if (NavigationView.this.mBreadcrumb != null) {
847 NavigationView.this.mBreadcrumb.startLoading();
848 } catch (Throwable ex) {
853 //Get the files, resolve links and apply configuration
854 //(sort, hidden, ...)
855 List<FileSystemObject> files = NavigationView.this.mFiles;
857 files = CommandHelper.listFiles(getContext(), fNewDir, null);
861 } catch (final ConsoleAllocException e) {
862 //Show exception and exists
863 NavigationView.this.post(new Runnable() {
866 Context ctx = getContext();
867 Log.e(TAG, ctx.getString(
868 R.string.msgs_cant_create_console), e);
869 DialogHelper.showToast(ctx,
870 R.string.msgs_cant_create_console,
872 ((Activity)ctx).finish();
877 } catch (Exception ex) {
878 //End of loading data
879 if (NavigationView.this.mBreadcrumb != null) {
881 NavigationView.this.mBreadcrumb.endLoading();
882 } catch (Throwable ex2) {
887 //Capture exception (attach task, and use listener to do the anim)
888 ExceptionUtil.attachAsyncTask(
890 new AsyncTask<Object, Integer, Boolean>() {
891 private List<FileSystemObject> mTaskFiles = null;
894 "unchecked", "unqualified-field-access"
896 protected Boolean doInBackground(Object... taskParams) {
897 mTaskFiles = (List<FileSystemObject>)taskParams[0];
902 @SuppressWarnings("unqualified-field-access")
903 protected void onPostExecute(Boolean result) {
904 if (!result.booleanValue()) {
908 mTaskFiles, addToHistory,
909 isNewHistory, hasChanged,
910 searchInfo, fNewDir, scrollTo);
913 final OnRelaunchCommandResult exListener =
914 new OnRelaunchCommandResult() {
916 public void onSuccess() {
920 public void onFailed(Throwable cause) {
924 public void onCancelled() {
927 private void done() {
930 synchronized (mSync) {
931 mChangingDir = false;
936 ExceptionUtil.translateException(
937 getContext(), ex, false, true, exListener);
946 protected void onPreExecute() {
955 protected void onPostExecute(List<FileSystemObject> files) {
956 // This means an exception. This method will be recalled then
959 files, addToHistory, isNewHistory,
960 hasChanged, searchInfo, fNewDir, scrollTo);
965 synchronized (mSync) {
966 mChangingDir = false;
973 * Method that performs a fade animation.
975 * @param out Fade out (true); Fade in (false)
977 void fadeEfect(final boolean out) {
978 Activity activity = (Activity)getContext();
979 activity.runOnUiThread(new Runnable() {
982 Animation fadeAnim = out ?
983 new AlphaAnimation(1, 0) :
984 new AlphaAnimation(0, 1);
985 fadeAnim.setDuration(50L);
986 fadeAnim.setFillAfter(true);
987 fadeAnim.setInterpolator(new AccelerateInterpolator());
988 NavigationView.this.startAnimation(fadeAnim);
993 task.execute(fNewDir);
998 * Method invoked when a execution ends.
1000 * @param files The files obtains from the list
1001 * @param addToHistory If add path to history
1002 * @param isNewHistory If is new history
1003 * @param hasChanged If current directory was changed
1004 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
1005 * @param newDir The new directory
1006 * @param scrollTo If not null, then listview must scroll to this item
1009 void onPostExecuteTask(
1010 List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
1011 boolean hasChanged, SearchInfoParcelable searchInfo,
1012 String newDir, final FileSystemObject scrollTo) {
1014 //Check that there is not errors and have some data
1015 if (files == null) {
1019 //Apply user preferences
1020 List<FileSystemObject> sortedFiles =
1021 FileHelper.applyUserPreferences(files, this.mRestrictions, this.mChRooted);
1023 //Remove parent directory if we are in the root of a chrooted environment
1024 if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
1025 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
1031 loadData(sortedFiles);
1032 this.mFiles = sortedFiles;
1033 if (searchInfo != null) {
1034 searchInfo.setSuccessNavigation(true);
1038 if (addToHistory && hasChanged && isNewHistory) {
1039 if (this.mOnHistoryListener != null) {
1040 //Communicate the need of a history change
1041 this.mOnHistoryListener.onNewHistory(onSaveState());
1045 //Change the breadcrumb
1046 if (this.mBreadcrumb != null) {
1047 this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
1051 if (scrollTo != null) {
1055 //The current directory is now the "newDir"
1056 this.mCurrentDir = newDir;
1057 if (this.mOnDirectoryChangedListener != null) {
1058 FileSystemObject dir = FileHelper.createFileSystemObject(new File(newDir));
1059 this.mOnDirectoryChangedListener.onDirectoryChanged(dir);
1062 //If calling activity is search, then save the search history
1063 if (searchInfo != null) {
1064 this.mOnHistoryListener.onNewHistory(searchInfo);
1067 //End of loading data
1069 NavigationView.this.mBreadcrumb.endLoading();
1070 } catch (Throwable ex) {
1077 * Method that loads the files in the adapter.
1079 * @param files The files to load in the adapter
1082 @SuppressWarnings("unchecked")
1083 private void loadData(final List<FileSystemObject> files) {
1084 //Notify data to adapter view
1085 final AdapterView<ListAdapter> view =
1086 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
1087 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
1088 adapter.setNotifyOnChange(false);
1090 adapter.addAll(files);
1091 adapter.notifyDataSetChanged();
1092 view.setSelection(0);
1099 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1100 // Different actions depending on user preference
1102 // Get the adapter and the fso
1103 FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
1104 FileSystemObject fso = adapter.getItem(position);
1106 // Parent directory hasn't actions
1107 if (fso instanceof ParentDirectory) {
1111 // Pick mode doesn't implements the onlongclick
1112 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
1117 return true; //Always consume the event
1121 * Method that opens or navigates to the {@link FileSystemObject}
1123 * @param fso The file system object
1125 public void open(FileSystemObject fso) {
1130 * Method that opens or navigates to the {@link FileSystemObject}
1132 * @param fso The file system object
1133 * @param searchInfo The search info
1135 public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
1136 // If is a folder, then navigate to
1137 if (FileHelper.isDirectory(fso)) {
1138 changeCurrentDir(fso.getFullPath(), searchInfo);
1140 // Open the file with the preferred registered app
1141 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1149 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1151 FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
1152 if (fso instanceof ParentDirectory) {
1153 changeCurrentDir(fso.getParent(), true, false, false, null, null);
1155 } else if (fso instanceof Directory) {
1156 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
1158 } else if (fso instanceof Symlink) {
1159 Symlink symlink = (Symlink)fso;
1160 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
1162 symlink.getLinkRef().getFullPath(), true, false, false, null, null);
1166 // Open the link ref
1167 fso = symlink.getLinkRef();
1170 // Open the file (edit or pick)
1171 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
1172 // Open the file with the preferred registered app
1173 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1175 // Request a file pick selection
1176 if (this.mOnFilePickedListener != null) {
1177 this.mOnFilePickedListener.onFilePicked(fso);
1180 } catch (Throwable ex) {
1181 ExceptionUtil.translateException(getContext(), ex);
1189 public void onRequestRefresh(Object o, boolean clearSelection) {
1190 if (o instanceof FileSystemObject) {
1191 refresh((FileSystemObject)o);
1192 } else if (o == null) {
1195 if (clearSelection) {
1204 public void onRequestRemove(Object o, boolean clearSelection) {
1205 if (o != null && o instanceof FileSystemObject) {
1206 removeItem((FileSystemObject)o);
1208 onRequestRefresh(null, clearSelection);
1210 if (clearSelection) {
1219 public void onNavigateTo(Object o) {
1227 public void onBreadcrumbItemClick(BreadcrumbItem item) {
1228 changeCurrentDir(item.getItemPath(), true, true, false, null, null);
1235 public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
1236 if (this.mOnNavigationSelectionChangedListener != null) {
1237 this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
1242 * Method invoked when a request to show the menu associated
1243 * with an item is started.
1245 * @param item The item for which the request was started
1247 public void onRequestMenu(final FileSystemObject item) {
1248 if (this.mOnNavigationRequestMenuListener != null) {
1249 this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
1257 public void onToggleSelection(FileSystemObject fso) {
1258 if (this.mAdapter != null) {
1259 this.mAdapter.toggleSelection(fso);
1267 public void onDeselectAll() {
1268 if (this.mAdapter != null) {
1269 this.mAdapter.deselectedAll();
1277 public void onSelectAllVisibleItems() {
1278 if (this.mAdapter != null) {
1279 this.mAdapter.selectedAllVisibleItems();
1287 public void onDeselectAllVisibleItems() {
1288 if (this.mAdapter != null) {
1289 this.mAdapter.deselectedAllVisibleItems();
1297 public List<FileSystemObject> onRequestSelectedFiles() {
1298 return this.getSelectedFiles();
1305 public List<FileSystemObject> onRequestCurrentItems() {
1306 return this.getFiles();
1313 public String onRequestCurrentDir() {
1314 return this.mCurrentDir;
1318 * Method that creates a ChRooted environment, protecting the user to break anything
1322 public void createChRooted() {
1323 // If we are in a ChRooted environment, then do nothing
1324 if (this.mChRooted) return;
1325 this.mChRooted = true;
1327 //Change to first storage volume
1328 StorageVolume[] volumes =
1329 StorageHelper.getStorageVolumes(getContext());
1330 if (volumes != null && volumes.length > 0) {
1331 changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1336 * Method that exits from a ChRooted environment
1339 public void exitChRooted() {
1340 // If we aren't in a ChRooted environment, then do nothing
1341 if (!this.mChRooted) return;
1342 this.mChRooted = false;
1349 * Method that ensures that the user don't go outside the ChRooted environment
1351 * @param newDir The new directory to navigate to
1354 private String checkChRootedNavigation(String newDir) {
1355 // If we aren't in ChRooted environment, then there is nothing to check
1356 if (!this.mChRooted) return newDir;
1358 // Check if the path is owned by one of the storage volumes
1359 if (!StorageHelper.isPathInStorageVolume(newDir)) {
1360 StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext());
1361 if (volumes != null && volumes.length > 0) {
1362 return volumes[0].getPath();
1369 * Method that applies the current theme to the activity
1371 public void applyTheme() {
1373 if (getBreadcrumb() != null) {
1374 getBreadcrumb().applyTheme();
1377 //- Redraw the adapter view
1378 Theme theme = ThemeManager.getCurrentTheme(getContext());
1379 theme.setBackgroundDrawable(getContext(), this, "background_drawable"); //$NON-NLS-1$
1380 if (this.mAdapter != null) {
1381 this.mAdapter.notifyThemeChanged();
1383 if (this.mAdapterView instanceof ListView) {
1384 ((ListView)this.mAdapterView).setDivider(
1385 theme.getDrawable(getContext(), "horizontal_divider_drawable")); //$NON-NLS-1$