OSDN Git Service

Remove console selection (Issue #17) - Part II (Settings & Code)
[android-x86/packages-apps-CMFileManager.git] / src / com / cyanogenmod / filemanager / ui / widgets / NavigationView.java
1 /*
2  * Copyright (C) 2012 The CyanogenMod Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.cyanogenmod.filemanager.ui.widgets;
18
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.widget.AdapterView;
29 import android.widget.ListAdapter;
30 import android.widget.RelativeLayout;
31 import android.widget.Toast;
32
33 import com.cyanogenmod.filemanager.FileManagerApplication;
34 import com.cyanogenmod.filemanager.R;
35 import com.cyanogenmod.filemanager.adapters.FileSystemObjectAdapter;
36 import com.cyanogenmod.filemanager.adapters.FileSystemObjectAdapter.OnSelectionChangedListener;
37 import com.cyanogenmod.filemanager.console.ConsoleAllocException;
38 import com.cyanogenmod.filemanager.listeners.OnHistoryListener;
39 import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
40 import com.cyanogenmod.filemanager.listeners.OnSelectionListener;
41 import com.cyanogenmod.filemanager.model.Directory;
42 import com.cyanogenmod.filemanager.model.FileSystemObject;
43 import com.cyanogenmod.filemanager.model.ParentDirectory;
44 import com.cyanogenmod.filemanager.model.Symlink;
45 import com.cyanogenmod.filemanager.parcelables.NavigationViewInfoParcelable;
46 import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
47 import com.cyanogenmod.filemanager.preferences.AccessMode;
48 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
49 import com.cyanogenmod.filemanager.preferences.NavigationLayoutMode;
50 import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
51 import com.cyanogenmod.filemanager.preferences.Preferences;
52 import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
53 import com.cyanogenmod.filemanager.util.CommandHelper;
54 import com.cyanogenmod.filemanager.util.DialogHelper;
55 import com.cyanogenmod.filemanager.util.ExceptionUtil;
56 import com.cyanogenmod.filemanager.util.FileHelper;
57 import com.cyanogenmod.filemanager.util.MimeTypeHelper;
58 import com.cyanogenmod.filemanager.util.StorageHelper;
59
60 import java.util.ArrayList;
61 import java.util.List;
62
63 /**
64  * The file manager implementation view (contains the graphical representation and the input
65  * management for a file manager; shows the folders/files, the mode view, react touch events,
66  * navigate, ...).
67  */
68 public class NavigationView extends RelativeLayout implements
69     AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener,
70     BreadcrumbListener, OnSelectionChangedListener, OnSelectionListener, OnRequestRefreshListener {
71
72     /**
73      * An interface to communicate selection changes events.
74      */
75     public interface OnNavigationSelectionChangedListener {
76         /**
77          * Method invoked when the selection changed.
78          *
79          * @param navView The navigation view that generate the event
80          * @param selectedItems The new selected items
81          */
82         void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems);
83     }
84
85     /**
86      * An interface to communicate a request for show the menu associated
87      * with an item.
88      */
89     public interface OnNavigationRequestMenuListener {
90         /**
91          * Method invoked when a request to show the menu associated
92          * with an item is started.
93          *
94          * @param navView The navigation view that generate the event
95          * @param item The item for which the request was started
96          */
97         void onRequestMenu(NavigationView navView, FileSystemObject item);
98     }
99
100     /**
101      * An interface to communicate a request when the user choose a file.
102      */
103     public interface OnFilePickedListener {
104         /**
105          * Method invoked when a request when the user choose a file.
106          *
107          * @param item The item choose
108          */
109         void onFilePicked(FileSystemObject item);
110     }
111
112     /**
113      * The navigation view mode
114      * @hide
115      */
116     public enum NAVIGATION_MODE {
117         /**
118          * The navigation view acts as a browser, and allow open files itself.
119          */
120         BROWSABLE,
121         /**
122          * The navigation view acts as a picker of files
123          */
124         PICKABLE,
125     }
126
127     private static final String TAG = "NavigationView"; //$NON-NLS-1$
128
129     private int mId;
130     private String mCurrentDir;
131     private NavigationLayoutMode mCurrentMode;
132     /**
133      * @hide
134      */
135     List<FileSystemObject> mFiles;
136     private FileSystemObjectAdapter mAdapter;
137
138     private final Object mSync = new Object();
139
140     private OnHistoryListener mOnHistoryListener;
141     private OnNavigationSelectionChangedListener mOnNavigationSelectionChangedListener;
142     private OnNavigationRequestMenuListener mOnNavigationRequestMenuListener;
143     private OnFilePickedListener mOnFilePickedListener;
144
145     private boolean mChRooted;
146
147     private NAVIGATION_MODE mNavigationMode;
148
149     private String mMimeType = MimeTypeHelper.ALL_MIME_TYPES;
150
151     /**
152      * @hide
153      */
154     Breadcrumb mBreadcrumb;
155     /**
156      * @hide
157      */
158     NavigationCustomTitleView mTitle;
159     /**
160      * @hide
161      */
162     AdapterView<?> mAdapterView;
163
164     //The layout for icons mode
165     private static final int RESOURCE_MODE_ICONS_LAYOUT = R.layout.navigation_view_icons;
166     private static final int RESOURCE_MODE_ICONS_ITEM = R.layout.navigation_view_icons_item;
167     //The layout for simple mode
168     private static final int RESOURCE_MODE_SIMPLE_LAYOUT = R.layout.navigation_view_simple;
169     private static final int RESOURCE_MODE_SIMPLE_ITEM = R.layout.navigation_view_simple_item;
170     //The layout for details mode
171     private static final int RESOURCE_MODE_DETAILS_LAYOUT = R.layout.navigation_view_details;
172     private static final int RESOURCE_MODE_DETAILS_ITEM = R.layout.navigation_view_details_item;
173
174     //The current layout identifier (is shared for all the mode layout)
175     private static final int RESOURCE_CURRENT_LAYOUT = R.id.navigation_view_layout;
176
177     /**
178      * Constructor of <code>NavigationView</code>.
179      *
180      * @param context The current context
181      * @param attrs The attributes of the XML tag that is inflating the view.
182      */
183     public NavigationView(Context context, AttributeSet attrs) {
184         super(context, attrs);
185         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Navigable);
186         try {
187             init(a);
188         } finally {
189             a.recycle();
190         }
191     }
192
193     /**
194      * Constructor of <code>NavigationView</code>.
195      *
196      * @param context The current context
197      * @param attrs The attributes of the XML tag that is inflating the view.
198      * @param defStyle The default style to apply to this view. If 0, no style
199      *        will be applied (beyond what is included in the theme). This may
200      *        either be an attribute resource, whose value will be retrieved
201      *        from the current theme, or an explicit style resource.
202      */
203     public NavigationView(Context context, AttributeSet attrs, int defStyle) {
204         super(context, attrs, defStyle);
205         TypedArray a = context.obtainStyledAttributes(
206                 attrs, R.styleable.Navigable, defStyle, 0);
207         try {
208             init(a);
209         } finally {
210             a.recycle();
211         }
212     }
213
214     /**
215      * Invoked when the instance need to be saved.
216      *
217      * @return NavigationViewInfoParcelable The serialized info
218      */
219     public NavigationViewInfoParcelable onSaveState() {
220         //Return the persistent the data
221         NavigationViewInfoParcelable parcel = new NavigationViewInfoParcelable();
222         parcel.setId(this.mId);
223         parcel.setCurrentDir(this.mCurrentDir);
224         parcel.setChRooted(this.mChRooted);
225         parcel.setSelectedFiles(this.mAdapter.getSelectedItems());
226         parcel.setFiles(this.mFiles);
227         return parcel;
228     }
229
230     /**
231      * Invoked when the instance need to be restored.
232      *
233      * @param info The serialized info
234      */
235     public void onRestoreState(NavigationViewInfoParcelable info) {
236         //Restore the data
237         this.mId = info.getId();
238         this.mCurrentDir = info.getCurrentDir();
239         this.mChRooted = info.getChRooted();
240         this.mFiles = info.getFiles();
241         this.mAdapter.setSelectedItems(info.getSelectedFiles());
242
243         //Update the views
244         refresh();
245     }
246
247     /**
248      * Method that initializes the view. This method loads all the necessary
249      * information and create an appropriate layout for the view.
250      *
251      * @param tarray The type array
252      */
253     private void init(TypedArray tarray) {
254         // Retrieve the mode
255         this.mNavigationMode = NAVIGATION_MODE.BROWSABLE;
256         int mode = tarray.getInteger(
257                                 R.styleable.Navigable_navigation,
258                                 NAVIGATION_MODE.BROWSABLE.ordinal());
259         if (mode >= 0 && mode < NAVIGATION_MODE.values().length) {
260             this.mNavigationMode = NAVIGATION_MODE.values()[mode];
261         }
262
263         //Initialize variables
264         this.mFiles = new ArrayList<FileSystemObject>();
265
266         // Is ChRooted environment?
267         if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
268             // Pick mode is always ChRooted
269             this.mChRooted = true;
270         } else {
271             this.mChRooted =
272                     FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
273         }
274
275         //Retrieve the default configuration
276         if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
277             SharedPreferences preferences = Preferences.getSharedPreferences();
278             int viewMode = preferences.getInt(
279                     FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
280                     ((ObjectIdentifier)FileManagerSettings.
281                             SETTINGS_LAYOUT_MODE.getDefaultValue()).getId());
282             changeViewMode(NavigationLayoutMode.fromId(viewMode));
283         } else {
284             // Pick mode has always a details layout
285             changeViewMode(NavigationLayoutMode.DETAILS);
286         }
287     }
288
289     /**
290      * Method that returns the mime/type used by this class. Only the files with this mime/type
291      * are shown.
292      *
293      * @return String The mime/type
294      */
295     public String getMimeType() {
296         return this.mMimeType;
297     }
298
299     /**
300      * Method that sets the mime/type used by this class. Only the files with this mime/type
301      * are shown.
302      *
303      * @param mimeType String The mime/type
304      */
305     public void setMimeType(String mimeType) {
306         this.mMimeType = mimeType;
307     }
308
309     /**
310      * Method that returns the current file list of the navigation view.
311      *
312      * @return List<FileSystemObject> The current file list of the navigation view
313      */
314     public List<FileSystemObject> getFiles() {
315         if (this.mFiles == null) {
316             return null;
317         }
318         return new ArrayList<FileSystemObject>(this.mFiles);
319     }
320
321     /**
322      * Method that returns the current file list of the navigation view.
323      *
324      * @return List<FileSystemObject> The current file list of the navigation view
325      */
326     public List<FileSystemObject> getSelectedFiles() {
327         if (this.mAdapter != null && this.mAdapter.getSelectedItems() != null) {
328             return new ArrayList<FileSystemObject>(this.mAdapter.getSelectedItems());
329         }
330         return null;
331     }
332
333     /**
334      * Method that returns the custom title fragment associated with this navigation view.
335      *
336      * @return NavigationCustomTitleView The custom title view fragment
337      */
338     public NavigationCustomTitleView getCustomTitle() {
339         return this.mTitle;
340     }
341
342     /**
343      * Method that associates the custom title fragment with this navigation view.
344      *
345      * @param title The custom title view fragment
346      */
347     public void setCustomTitle(NavigationCustomTitleView title) {
348         this.mTitle = title;
349     }
350
351     /**
352      * Method that returns the breadcrumb associated with this navigation view.
353      *
354      * @return Breadcrumb The breadcrumb view fragment
355      */
356     public Breadcrumb getBreadcrumb() {
357         return this.mBreadcrumb;
358     }
359
360     /**
361      * Method that associates the breadcrumb with this navigation view.
362      *
363      * @param breadcrumb The breadcrumb view fragment
364      */
365     public void setBreadcrumb(Breadcrumb breadcrumb) {
366         this.mBreadcrumb = breadcrumb;
367         this.mBreadcrumb.addBreadcrumbListener(this);
368     }
369
370     /**
371      * Method that sets the listener for communicate history changes.
372      *
373      * @param onHistoryListener The listener for communicate history changes
374      */
375     public void setOnHistoryListener(OnHistoryListener onHistoryListener) {
376         this.mOnHistoryListener = onHistoryListener;
377     }
378
379     /**
380      * Method that sets the listener which communicates selection changes.
381      *
382      * @param onNavigationSelectionChangedListener The listener reference
383      */
384     public void setOnNavigationSelectionChangedListener(
385             OnNavigationSelectionChangedListener onNavigationSelectionChangedListener) {
386         this.mOnNavigationSelectionChangedListener = onNavigationSelectionChangedListener;
387     }
388
389     /**
390      * Method that sets the listener for menu item requests.
391      *
392      * @param onNavigationRequestMenuListener The listener reference
393      */
394     public void setOnNavigationOnRequestMenuListener(
395             OnNavigationRequestMenuListener onNavigationRequestMenuListener) {
396         this.mOnNavigationRequestMenuListener = onNavigationRequestMenuListener;
397     }
398
399     /**
400      * @return the mOnFilePickedListener
401      */
402     public OnFilePickedListener getOnFilePickedListener() {
403         return this.mOnFilePickedListener;
404     }
405
406     /**
407      * Method that sets the listener for picked items
408      *
409      * @param onFilePickedListener The listener reference
410      */
411     public void setOnFilePickedListener(OnFilePickedListener onFilePickedListener) {
412         this.mOnFilePickedListener = onFilePickedListener;
413     }
414
415     /**
416      * Method that forces the view to scroll to the file system object passed.
417      *
418      * @param fso The file system object
419      */
420     public void scrollTo(FileSystemObject fso) {
421         if (fso != null) {
422             try {
423                 int position = this.mAdapter.getPosition(fso);
424                 this.mAdapterView.setSelection(position);
425             } catch (Exception e) {
426                 this.mAdapterView.setSelection(0);
427             }
428         }
429     }
430
431     /**
432      * Method that refresh the view data.
433      */
434     public void refresh() {
435         FileSystemObject fso = null;
436         // Try to restore the previous scroll position
437         try {
438             if (this.mAdapterView != null && this.mAdapter != null) {
439                 int position = this.mAdapterView.getFirstVisiblePosition();
440                 fso = this.mAdapter.getItem(position);
441             }
442         } catch (Throwable _throw) {/**NON BLOCK**/}
443         refresh(fso);
444     }
445
446     /**
447      * Method that refresh the view data.
448      *
449      * @param scrollTo Scroll to object
450      */
451     public void refresh(FileSystemObject scrollTo) {
452         //Check that current directory was set
453         if (this.mCurrentDir == null || this.mFiles == null) {
454             return;
455         }
456
457         //Reload data
458         changeCurrentDir(this.mCurrentDir, false, true, false, null, scrollTo);
459     }
460
461     /**
462      * Method that change the view mode.
463      *
464      * @param newMode The new mode
465      */
466     @SuppressWarnings({ "unchecked", "null" })
467     public void changeViewMode(final NavigationLayoutMode newMode) {
468         synchronized (this.mSync) {
469             //Check that it is really necessary change the mode
470             if (this.mCurrentMode != null && this.mCurrentMode.compareTo(newMode) == 0) {
471                 return;
472             }
473
474             //Creates the new layout
475             AdapterView<ListAdapter> newView = null;
476             int itemResourceId = -1;
477             if (newMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
478                 newView = (AdapterView<ListAdapter>)inflate(
479                         getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
480                 itemResourceId = RESOURCE_MODE_ICONS_ITEM;
481             } else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
482                 newView =  (AdapterView<ListAdapter>)inflate(
483                         getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
484                 itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
485             } else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
486                 newView =  (AdapterView<ListAdapter>)inflate(
487                         getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
488                 itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
489             }
490
491             //Get the current adapter and its adapter list
492             List<FileSystemObject> files = new ArrayList<FileSystemObject>(this.mFiles);
493             final AdapterView<ListAdapter> current =
494                     (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
495             FileSystemObjectAdapter adapter =
496                     new FileSystemObjectAdapter(
497                             getContext(),
498                             new ArrayList<FileSystemObject>(),
499                             itemResourceId,
500                             this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0);
501             adapter.setOnSelectionChangedListener(this);
502
503             //Remove current layout
504             if (current != null) {
505                 if (current.getAdapter() != null) {
506                     //Save selected items before dispose adapter
507                     FileSystemObjectAdapter currentAdapter =
508                             ((FileSystemObjectAdapter)current.getAdapter());
509                     adapter.setSelectedItems(currentAdapter.getSelectedItems());
510                     currentAdapter.dispose();
511                 }
512                 removeView(current);
513             }
514             this.mFiles = files;
515             adapter.addAll(files);
516             adapter.notifyDataSetChanged();
517
518             //Set the adapter
519             this.mAdapter = adapter;
520             newView.setAdapter(this.mAdapter);
521             newView.setOnItemClickListener(NavigationView.this);
522
523             //Add the new layout
524             this.mAdapterView = newView;
525             addView(newView, 0);
526             this.mCurrentMode = newMode;
527
528             // Pick mode doesn't implements the onlongclick
529             if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
530                 this.mAdapterView.setOnItemLongClickListener(this);
531             } else {
532                 this.mAdapterView.setOnItemLongClickListener(null);
533             }
534
535             //Save the preference (only in navigation browse mode)
536             if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
537                 try {
538                     Preferences.savePreference(
539                             FileManagerSettings.SETTINGS_LAYOUT_MODE, newMode, true);
540                 } catch (Exception ex) {
541                     Log.e(TAG, "Save of view mode preference fails", ex); //$NON-NLS-1$
542                 }
543             }
544         }
545     }
546
547     /**
548      * Method that removes a {@link FileSystemObject} from the view
549      *
550      * @param fso The file system object
551      */
552     public void removeItem(FileSystemObject fso) {
553         this.mAdapter.remove(fso);
554         this.mAdapter.notifyDataSetChanged();
555     }
556
557     /**
558      * Method that removes a file system object from his path from the view
559      *
560      * @param path The file system object path
561      */
562     public void removeItem(String path) {
563         FileSystemObject fso = this.mAdapter.getItem(path);
564         if (fso != null) {
565             this.mAdapter.remove(fso);
566             this.mAdapter.notifyDataSetChanged();
567         }
568     }
569
570     /**
571      * Method that returns the current directory.
572      *
573      * @return String The current directory
574      */
575     public String getCurrentDir() {
576         return this.mCurrentDir;
577     }
578
579     /**
580      * Method that changes the current directory of the view.
581      *
582      * @param newDir The new directory location
583      */
584     public void changeCurrentDir(final String newDir) {
585         changeCurrentDir(newDir, true, false, false, null, null);
586     }
587
588     /**
589      * Method that changes the current directory of the view.
590      *
591      * @param newDir The new directory location
592      * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
593      */
594     public void changeCurrentDir(final String newDir, SearchInfoParcelable searchInfo) {
595         changeCurrentDir(newDir, true, false, false, searchInfo, null);
596     }
597
598     /**
599      * Method that changes the current directory of the view.
600      *
601      * @param newDir The new directory location
602      * @param addToHistory Add the directory to history
603      * @param reload Force the reload of the data
604      * @param useCurrent If this method must use the actual data (for back actions)
605      * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
606      * @param scrollTo If not null, then listview must scroll to this item
607      */
608     private void changeCurrentDir(
609             final String newDir, final boolean addToHistory,
610             final boolean reload, final boolean useCurrent,
611             final SearchInfoParcelable searchInfo, final FileSystemObject scrollTo) {
612
613         // Check navigation security (don't allow to go outside the ChRooted environment if one
614         // is created)
615         final String fNewDir = checkChRootedNavigation(newDir);
616
617         synchronized (this.mSync) {
618             //Check that it is really necessary change the directory
619             if (!reload && this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0) {
620                 return;
621             }
622
623             final boolean hasChanged =
624                     !(this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0);
625             final boolean isNewHistory = (this.mCurrentDir != null);
626
627             //Execute the listing in a background process
628             AsyncTask<String, Integer, List<FileSystemObject>> task =
629                     new AsyncTask<String, Integer, List<FileSystemObject>>() {
630                         /**
631                          * {@inheritDoc}
632                          */
633                         @Override
634                         protected List<FileSystemObject> doInBackground(String... params) {
635                             try {
636                                 //Reset the custom title view and returns to breadcrumb
637                                 if (NavigationView.this.mTitle != null) {
638                                     NavigationView.this.mTitle.post(new Runnable() {
639                                         @Override
640                                         public void run() {
641                                             try {
642                                                 NavigationView.this.mTitle.restoreView();
643                                             } catch (Exception e) {
644                                                 e.printStackTrace();
645                                             }
646                                         }
647                                     });
648                                 }
649
650
651                                 //Start of loading data
652                                 if (NavigationView.this.mBreadcrumb != null) {
653                                     try {
654                                         NavigationView.this.mBreadcrumb.startLoading();
655                                     } catch (Throwable ex) {
656                                         /**NON BLOCK**/
657                                     }
658                                 }
659
660                                 //Get the files, resolve links and apply configuration
661                                 //(sort, hidden, ...)
662                                 List<FileSystemObject> files = NavigationView.this.mFiles;
663                                 if (!useCurrent) {
664                                     files = CommandHelper.listFiles(getContext(), fNewDir, null);
665                                 }
666                                 return files;
667                             } catch (final ConsoleAllocException e) {
668                                 //Show exception and exists
669                                 NavigationView.this.post(new Runnable() {
670                                     @Override
671                                     public void run() {
672                                         Context ctx = getContext();
673                                         Log.e(TAG, ctx.getString(
674                                                 R.string.msgs_cant_create_console), e);
675                                         DialogHelper.showToast(ctx,
676                                                 R.string.msgs_cant_create_console,
677                                                 Toast.LENGTH_LONG);
678                                         ((Activity)ctx).finish();
679                                     }
680                                 });
681                                 return null;
682
683                             } catch (Exception ex) {
684                                 //End of loading data
685                                 if (NavigationView.this.mBreadcrumb != null) {
686                                     try {
687                                         NavigationView.this.mBreadcrumb.endLoading();
688                                     } catch (Throwable ex2) {
689                                         /**NON BLOCK**/
690                                     }
691                                 }
692
693                                 //Capture exception
694                                 ExceptionUtil.attachAsyncTask(
695                                     ex,
696                                     new AsyncTask<Object, Integer, Boolean>() {
697                                         @Override
698                                         @SuppressWarnings("unchecked")
699                                         protected Boolean doInBackground(Object... taskParams) {
700                                             final List<FileSystemObject> files =
701                                                     (List<FileSystemObject>)taskParams[0];
702                                             NavigationView.this.mAdapterView.post(
703                                                     new Runnable() {
704                                                         @Override
705                                                         public void run() {
706                                                             onPostExecuteTask(
707                                                                     files, addToHistory,
708                                                                     isNewHistory, hasChanged,
709                                                                     searchInfo, fNewDir, scrollTo);
710                                                         }
711                                                     });
712                                             return Boolean.TRUE;
713                                         }
714
715                                     });
716                                 ExceptionUtil.translateException(getContext(), ex);
717                             }
718                             return null;
719                         }
720
721                         /**
722                          * {@inheritDoc}
723                          */
724                         @Override
725                         protected void onPostExecute(List<FileSystemObject> files) {
726                             onPostExecuteTask(
727                                     files, addToHistory, isNewHistory,
728                                     hasChanged, searchInfo, fNewDir, scrollTo);
729                         }
730                    };
731             task.execute(fNewDir);
732         }
733     }
734
735
736     /**
737      * Method invoked when a execution ends.
738      *
739      * @param files The files obtains from the list
740      * @param addToHistory If add path to history
741      * @param isNewHistory If is new history
742      * @param hasChanged If current directory was changed
743      * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
744      * @param newDir The new directory
745      * @param scrollTo If not null, then listview must scroll to this item
746      * @hide
747      */
748     void onPostExecuteTask(
749             List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
750             boolean hasChanged, SearchInfoParcelable searchInfo,
751             String newDir, final FileSystemObject scrollTo) {
752         try {
753             //Check that there is not errors and have some data
754             if (files == null) {
755                 return;
756             }
757
758             //Apply user preferences
759             List<FileSystemObject> sortedFiles =
760                     FileHelper.applyUserPreferences(files, this.mMimeType, this.mChRooted);
761
762             //Remove parent directory if we are in the root of a chrooted environment
763             if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
764                 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
765                     files.remove(0);
766                 }
767             }
768
769             //Load the data
770             loadData(sortedFiles);
771             NavigationView.this.mFiles = sortedFiles;
772             if (searchInfo != null) {
773                 searchInfo.setSuccessNavigation(true);
774             }
775
776             //Add to history?
777             if (addToHistory && hasChanged && isNewHistory) {
778                 if (NavigationView.this.mOnHistoryListener != null) {
779                     //Communicate the need of a history change
780                     NavigationView.this.mOnHistoryListener.onNewHistory(onSaveState());
781                 }
782             }
783
784             //Change the breadcrumb
785             if (NavigationView.this.mBreadcrumb != null) {
786                 NavigationView.this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
787             }
788
789             //Scroll to object?
790             if (scrollTo != null) {
791                 scrollTo(scrollTo);
792             }
793
794             //The current directory is now the "newDir"
795             NavigationView.this.mCurrentDir = newDir;
796
797         } finally {
798             //If calling activity is search, then save the search history
799             if (searchInfo != null) {
800                 NavigationView.this.mOnHistoryListener.onNewHistory(searchInfo);
801             }
802
803             //End of loading data
804             try {
805                 NavigationView.this.mBreadcrumb.endLoading();
806             } catch (Throwable ex) {
807                 /**NON BLOCK**/
808             }
809         }
810     }
811
812     /**
813      * Method that loads the files in the adapter.
814      *
815      * @param files The files to load in the adapter
816      * @hide
817      */
818     @SuppressWarnings("unchecked")
819     private void loadData(final List<FileSystemObject> files) {
820         //Notify data to adapter view
821         final AdapterView<ListAdapter> view =
822                 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
823         FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
824         adapter.clear();
825         adapter.addAll(files);
826         adapter.notifyDataSetChanged();
827         view.setSelection(0);
828     }
829
830     /**
831      * {@inheritDoc}
832      */
833     @Override
834     public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
835         // Different actions depending on user preference
836
837         // Get the adapter and the fso
838         FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
839         FileSystemObject fso = adapter.getItem(position);
840
841         // Parent directory hasn't actions
842         if (fso instanceof ParentDirectory) {
843             return false;
844         }
845
846         // Pick mode doesn't implements the onlongclick
847         if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
848             return false;
849         }
850
851         onRequestMenu(fso);
852         return true; //Always consume the event
853     }
854
855     /**
856      * Method that opens or navigates to the {@link FileSystemObject}
857      *
858      * @param fso The file system object
859      */
860     public void open(FileSystemObject fso) {
861         open(fso, null);
862     }
863
864     /**
865      * Method that opens or navigates to the {@link FileSystemObject}
866      *
867      * @param fso The file system object
868      * @param searchInfo The search info
869      */
870     public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
871         // If is a folder, then navigate to
872         if (FileHelper.isDirectory(fso)) {
873             changeCurrentDir(fso.getFullPath(), searchInfo);
874         } else {
875             // Open the file with the preferred registered app
876             IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
877         }
878     }
879
880     /**
881      * {@inheritDoc}
882      */
883     @Override
884     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
885         try {
886             FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
887             if (fso instanceof ParentDirectory) {
888                 changeCurrentDir(fso.getParent(), true, false, false, null, null);
889             } else if (fso instanceof Directory) {
890                 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
891             } else if (fso instanceof Symlink) {
892                 Symlink symlink = (Symlink)fso;
893                 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
894                     changeCurrentDir(
895                             symlink.getLinkRef().getFullPath(), true, false, false, null, null);
896                 }
897             } else {
898                 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
899                     // Open the file with the preferred registered app
900                     IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
901                 } else {
902                     // Request a file pick selection
903                     if (this.mOnFilePickedListener != null) {
904                         this.mOnFilePickedListener.onFilePicked(fso);
905                     }
906                 }
907             }
908         } catch (Throwable ex) {
909             ExceptionUtil.translateException(getContext(), ex);
910         }
911     }
912
913     /**
914      * {@inheritDoc}
915      */
916     @Override
917     public void onRequestRefresh(Object o) {
918         if (o instanceof FileSystemObject) {
919             refresh((FileSystemObject)o);
920         }
921         onDeselectAll();
922     }
923
924     /**
925      * {@inheritDoc}
926      */
927     @Override
928     public void onRequestRemove(Object o) {
929         if (o instanceof FileSystemObject) {
930             removeItem((FileSystemObject)o);
931         }
932         onDeselectAll();
933     }
934
935     /**
936      * {@inheritDoc}
937      */
938     @Override
939     public void onNavigateTo(Object o) {
940         // Ignored
941     }
942
943     /**
944      * {@inheritDoc}
945      */
946     @Override
947     public void onBreadcrumbItemClick(BreadcrumbItem item) {
948         changeCurrentDir(item.getItemPath(), true, true, false, null, null);
949     }
950
951     /**
952      * {@inheritDoc}
953      */
954     @Override
955     public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
956         if (this.mOnNavigationSelectionChangedListener != null) {
957             this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
958         }
959     }
960
961     /**
962      * Method invoked when a request to show the menu associated
963      * with an item is started.
964      *
965      * @param item The item for which the request was started
966      */
967     public void onRequestMenu(final FileSystemObject item) {
968         if (this.mOnNavigationRequestMenuListener != null) {
969             this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
970         }
971     }
972
973     /**
974      * {@inheritDoc}
975      */
976     @Override
977     public void onToggleSelection(FileSystemObject fso) {
978         if (this.mAdapter != null) {
979             this.mAdapter.toggleSelection(fso);
980         }
981     }
982
983     /**
984      * {@inheritDoc}
985      */
986     @Override
987     public void onDeselectAll() {
988         if (this.mAdapter != null) {
989             this.mAdapter.deselectedAll();
990         }
991     }
992
993     /**
994      * {@inheritDoc}
995      */
996     @Override
997     public void onSelectAllVisibleItems() {
998         if (this.mAdapter != null) {
999             this.mAdapter.selectedAllVisibleItems();
1000         }
1001     }
1002
1003     /**
1004      * {@inheritDoc}
1005      */
1006     @Override
1007     public void onDeselectAllVisibleItems() {
1008         if (this.mAdapter != null) {
1009             this.mAdapter.deselectedAllVisibleItems();
1010         }
1011     }
1012
1013     /**
1014      * {@inheritDoc}
1015      */
1016     @Override
1017     public List<FileSystemObject> onRequestSelectedFiles() {
1018         return this.getSelectedFiles();
1019     }
1020
1021     /**
1022      * {@inheritDoc}
1023      */
1024     @Override
1025     public List<FileSystemObject> onRequestCurrentItems() {
1026         return this.getFiles();
1027     }
1028
1029     /**
1030      * {@inheritDoc}
1031      */
1032     @Override
1033     public String onRequestCurrentDir() {
1034         return this.mCurrentDir;
1035     }
1036
1037     /**
1038      * Method that creates a ChRooted environment, protecting the user to break anything
1039      * in the device
1040      * @hide
1041      */
1042     public void createChRooted() {
1043         // If we are in a ChRooted environment, then do nothing
1044         if (this.mChRooted) return;
1045         this.mChRooted = true;
1046
1047         //Change to first storage volume
1048         StorageVolume[] volumes =
1049                 StorageHelper.getStorageVolumes(getContext());
1050         if (volumes != null && volumes.length > 0) {
1051             changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1052         }
1053     }
1054
1055     /**
1056      * Method that exits from a ChRooted environment
1057      * @hide
1058      */
1059     public void exitChRooted() {
1060         // If we aren't in a ChRooted environment, then do nothing
1061         if (!this.mChRooted) return;
1062         this.mChRooted = false;
1063
1064         // Refresh
1065         refresh();
1066     }
1067
1068     /**
1069      * Method that ensures that the user don't go outside the ChRooted environment
1070      *
1071      * @param newDir The new directory to navigate to
1072      * @return String
1073      */
1074     private String checkChRootedNavigation(String newDir) {
1075         // If we aren't in ChRooted environment, then there is nothing to check
1076         if (!this.mChRooted) return newDir;
1077
1078         // Check if the path is owned by one of the storage volumes
1079         if (!StorageHelper.isPathInStorageVolume(newDir)) {
1080             StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext());
1081             if (volumes != null && volumes.length > 0) {
1082                 return volumes[0].getPath();
1083             }
1084         }
1085         return newDir;
1086     }
1087
1088 }