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;
70 import java.util.ArrayList;
71 import java.util.HashMap;
72 import java.util.List;
76 * The file manager implementation view (contains the graphical representation and the input
77 * management for a file manager; shows the folders/files, the mode view, react touch events,
80 public class NavigationView extends RelativeLayout implements
81 AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener,
82 BreadcrumbListener, OnSelectionChangedListener, OnSelectionListener, OnRequestRefreshListener {
84 private static final String TAG = "NavigationView"; //$NON-NLS-1$
87 * An interface to communicate selection changes events.
89 public interface OnNavigationSelectionChangedListener {
91 * Method invoked when the selection changed.
93 * @param navView The navigation view that generate the event
94 * @param selectedItems The new selected items
96 void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems);
100 * An interface to communicate a request for show the menu associated
103 public interface OnNavigationRequestMenuListener {
105 * Method invoked when a request to show the menu associated
106 * with an item is started.
108 * @param navView The navigation view that generate the event
109 * @param item The item for which the request was started
111 void onRequestMenu(NavigationView navView, FileSystemObject item);
115 * An interface to communicate a request when the user choose a file.
117 public interface OnFilePickedListener {
119 * Method invoked when a request when the user choose a file.
121 * @param item The item choose
123 void onFilePicked(FileSystemObject item);
127 * The navigation view mode
130 public enum NAVIGATION_MODE {
132 * The navigation view acts as a browser, and allow open files itself.
136 * The navigation view acts as a picker of files
142 * A listener for flinging events from {@link FlingerListView}
144 private final OnItemFlingerListener mOnItemFlingerListener = new OnItemFlingerListener() {
147 public boolean onItemFlingerStart(
148 AdapterView<?> parent, View view, int position, long id) {
150 // Response if the item can be removed
151 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
152 FileSystemObject fso = adapter.getItem(position);
154 if (fso instanceof ParentDirectory) {
159 } catch (Exception e) {
160 ExceptionUtil.translateException(getContext(), e, true, false);
166 public void onItemFlingerEnd(OnItemFlingerResponder responder,
167 AdapterView<?> parent, View view, int position, long id) {
170 // Response if the item can be removed
171 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)parent.getAdapter();
172 FileSystemObject fso = adapter.getItem(position);
174 DeleteActionPolicy.removeFileSystemObject(
183 // Cancels the flinger operation
186 } catch (Exception e) {
187 ExceptionUtil.translateException(getContext(), e, true, false);
194 private String mCurrentDir;
195 private NavigationLayoutMode mCurrentMode;
199 List<FileSystemObject> mFiles;
200 private FileSystemObjectAdapter mAdapter;
202 private final Object mSync = new Object();
204 private OnHistoryListener mOnHistoryListener;
205 private OnNavigationSelectionChangedListener mOnNavigationSelectionChangedListener;
206 private OnNavigationRequestMenuListener mOnNavigationRequestMenuListener;
207 private OnFilePickedListener mOnFilePickedListener;
209 private boolean mChRooted;
211 private NAVIGATION_MODE mNavigationMode;
214 private Map<DisplayRestrictions, Object> mRestrictions;
219 Breadcrumb mBreadcrumb;
223 NavigationCustomTitleView mTitle;
227 AdapterView<?> mAdapterView;
229 //The layout for icons mode
230 private static final int RESOURCE_MODE_ICONS_LAYOUT = R.layout.navigation_view_icons;
231 private static final int RESOURCE_MODE_ICONS_ITEM = R.layout.navigation_view_icons_item;
232 //The layout for simple mode
233 private static final int RESOURCE_MODE_SIMPLE_LAYOUT = R.layout.navigation_view_simple;
234 private static final int RESOURCE_MODE_SIMPLE_ITEM = R.layout.navigation_view_simple_item;
235 //The layout for details mode
236 private static final int RESOURCE_MODE_DETAILS_LAYOUT = R.layout.navigation_view_details;
237 private static final int RESOURCE_MODE_DETAILS_ITEM = R.layout.navigation_view_details_item;
239 //The current layout identifier (is shared for all the mode layout)
240 private static final int RESOURCE_CURRENT_LAYOUT = R.id.navigation_view_layout;
243 * Constructor of <code>NavigationView</code>.
245 * @param context The current context
246 * @param attrs The attributes of the XML tag that is inflating the view.
248 public NavigationView(Context context, AttributeSet attrs) {
249 super(context, attrs);
250 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Navigable);
259 * Constructor of <code>NavigationView</code>.
261 * @param context The current context
262 * @param attrs The attributes of the XML tag that is inflating the view.
263 * @param defStyle The default style to apply to this view. If 0, no style
264 * will be applied (beyond what is included in the theme). This may
265 * either be an attribute resource, whose value will be retrieved
266 * from the current theme, or an explicit style resource.
268 public NavigationView(Context context, AttributeSet attrs, int defStyle) {
269 super(context, attrs, defStyle);
270 TypedArray a = context.obtainStyledAttributes(
271 attrs, R.styleable.Navigable, defStyle, 0);
280 * Invoked when the instance need to be saved.
282 * @return NavigationViewInfoParcelable The serialized info
284 public NavigationViewInfoParcelable onSaveState() {
285 //Return the persistent the data
286 NavigationViewInfoParcelable parcel = new NavigationViewInfoParcelable();
287 parcel.setId(this.mId);
288 parcel.setCurrentDir(this.mCurrentDir);
289 parcel.setChRooted(this.mChRooted);
290 parcel.setSelectedFiles(this.mAdapter.getSelectedItems());
291 parcel.setFiles(this.mFiles);
296 * Invoked when the instance need to be restored.
298 * @param info The serialized info
300 public void onRestoreState(NavigationViewInfoParcelable info) {
302 this.mId = info.getId();
303 this.mCurrentDir = info.getCurrentDir();
304 this.mChRooted = info.getChRooted();
305 this.mFiles = info.getFiles();
306 this.mAdapter.setSelectedItems(info.getSelectedFiles());
313 * Method that initializes the view. This method loads all the necessary
314 * information and create an appropriate layout for the view.
316 * @param tarray The type array
318 private void init(TypedArray tarray) {
320 this.mNavigationMode = NAVIGATION_MODE.BROWSABLE;
321 int mode = tarray.getInteger(
322 R.styleable.Navigable_navigation,
323 NAVIGATION_MODE.BROWSABLE.ordinal());
324 if (mode >= 0 && mode < NAVIGATION_MODE.values().length) {
325 this.mNavigationMode = NAVIGATION_MODE.values()[mode];
328 // Initialize default restrictions (no restrictions)
329 this.mRestrictions = new HashMap<DisplayRestrictions, Object>();
331 //Initialize variables
332 this.mFiles = new ArrayList<FileSystemObject>();
334 // Is ChRooted environment?
335 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
336 // Pick mode is always ChRooted
337 this.mChRooted = true;
340 FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
343 //Retrieve the default configuration
344 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
345 SharedPreferences preferences = Preferences.getSharedPreferences();
346 int viewMode = preferences.getInt(
347 FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
348 ((ObjectIdentifier)FileManagerSettings.
349 SETTINGS_LAYOUT_MODE.getDefaultValue()).getId());
350 changeViewMode(NavigationLayoutMode.fromId(viewMode));
352 // Pick mode has always a details layout
353 changeViewMode(NavigationLayoutMode.DETAILS);
358 * Method that returns the display restrictions to apply to this view.
360 * @return Map<DisplayRestrictions, Object> The restrictions to apply
362 public Map<DisplayRestrictions, Object> getRestrictions() {
363 return this.mRestrictions;
367 * Method that sets the display restrictions to apply to this view.
369 * @param mRestrictions The restrictions to apply
371 public void setRestrictions(Map<DisplayRestrictions, Object> mRestrictions) {
372 this.mRestrictions = mRestrictions;
376 * Method that returns the current file list of the navigation view.
378 * @return List<FileSystemObject> The current file list of the navigation view
380 public List<FileSystemObject> getFiles() {
381 if (this.mFiles == null) {
384 return new ArrayList<FileSystemObject>(this.mFiles);
388 * Method that returns the current file list of the navigation view.
390 * @return List<FileSystemObject> The current file list of the navigation view
392 public List<FileSystemObject> getSelectedFiles() {
393 if (this.mAdapter != null && this.mAdapter.getSelectedItems() != null) {
394 return new ArrayList<FileSystemObject>(this.mAdapter.getSelectedItems());
400 * Method that returns the custom title fragment associated with this navigation view.
402 * @return NavigationCustomTitleView The custom title view fragment
404 public NavigationCustomTitleView getCustomTitle() {
409 * Method that associates the custom title fragment with this navigation view.
411 * @param title The custom title view fragment
413 public void setCustomTitle(NavigationCustomTitleView title) {
418 * Method that returns the breadcrumb associated with this navigation view.
420 * @return Breadcrumb The breadcrumb view fragment
422 public Breadcrumb getBreadcrumb() {
423 return this.mBreadcrumb;
427 * Method that associates the breadcrumb with this navigation view.
429 * @param breadcrumb The breadcrumb view fragment
431 public void setBreadcrumb(Breadcrumb breadcrumb) {
432 this.mBreadcrumb = breadcrumb;
433 this.mBreadcrumb.addBreadcrumbListener(this);
437 * Method that sets the listener for communicate history changes.
439 * @param onHistoryListener The listener for communicate history changes
441 public void setOnHistoryListener(OnHistoryListener onHistoryListener) {
442 this.mOnHistoryListener = onHistoryListener;
446 * Method that sets the listener which communicates selection changes.
448 * @param onNavigationSelectionChangedListener The listener reference
450 public void setOnNavigationSelectionChangedListener(
451 OnNavigationSelectionChangedListener onNavigationSelectionChangedListener) {
452 this.mOnNavigationSelectionChangedListener = onNavigationSelectionChangedListener;
456 * Method that sets the listener for menu item requests.
458 * @param onNavigationRequestMenuListener The listener reference
460 public void setOnNavigationOnRequestMenuListener(
461 OnNavigationRequestMenuListener onNavigationRequestMenuListener) {
462 this.mOnNavigationRequestMenuListener = onNavigationRequestMenuListener;
466 * @return the mOnFilePickedListener
468 public OnFilePickedListener getOnFilePickedListener() {
469 return this.mOnFilePickedListener;
473 * Method that sets the listener for picked items
475 * @param onFilePickedListener The listener reference
477 public void setOnFilePickedListener(OnFilePickedListener onFilePickedListener) {
478 this.mOnFilePickedListener = onFilePickedListener;
482 * Method that sets if the view should use flinger gesture detection.
484 * @param useFlinger If the view should use flinger gesture detection
486 public void setUseFlinger(boolean useFlinger) {
487 if (this.mCurrentMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
491 // Set the flinger listener (only when navigate)
492 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
493 if (this.mAdapterView instanceof FlingerListView) {
495 ((FlingerListView)this.mAdapterView).
496 setOnItemFlingerListener(this.mOnItemFlingerListener);
498 ((FlingerListView)this.mAdapterView).setOnItemFlingerListener(null);
505 * Method that forces the view to scroll to the file system object passed.
507 * @param fso The file system object
509 public void scrollTo(FileSystemObject fso) {
512 int position = this.mAdapter.getPosition(fso);
513 this.mAdapterView.setSelection(position);
514 } catch (Exception e) {
515 this.mAdapterView.setSelection(0);
518 this.mAdapterView.setSelection(0);
523 * Method that refresh the view data.
525 public void refresh() {
530 * Method that refresh the view data.
532 * @param restore Restore previous position
534 public void refresh(boolean restore) {
535 FileSystemObject fso = null;
536 // Try to restore the previous scroll position
539 if (this.mAdapterView != null && this.mAdapter != null) {
540 int position = this.mAdapterView.getFirstVisiblePosition();
541 fso = this.mAdapter.getItem(position);
543 } catch (Throwable _throw) {/**NON BLOCK**/}
549 * Method that refresh the view data.
551 * @param scrollTo Scroll to object
553 public void refresh(FileSystemObject scrollTo) {
554 //Check that current directory was set
555 if (this.mCurrentDir == null || this.mFiles == null) {
560 changeCurrentDir(this.mCurrentDir, false, true, false, null, scrollTo);
564 * Method that change the view mode.
566 * @param newMode The new mode
568 @SuppressWarnings({ "unchecked", "null" })
569 public void changeViewMode(final NavigationLayoutMode newMode) {
570 synchronized (this.mSync) {
571 //Check that it is really necessary change the mode
572 if (this.mCurrentMode != null && this.mCurrentMode.compareTo(newMode) == 0) {
576 // If we should set the listview to response to flinger gesture detection
578 Preferences.getSharedPreferences().getBoolean(
579 FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
580 ((Boolean)FileManagerSettings.
581 SETTINGS_USE_FLINGER.
582 getDefaultValue()).booleanValue());
584 //Creates the new layout
585 AdapterView<ListAdapter> newView = null;
586 int itemResourceId = -1;
587 if (newMode.compareTo(NavigationLayoutMode.ICONS) == 0) {
588 newView = (AdapterView<ListAdapter>)inflate(
589 getContext(), RESOURCE_MODE_ICONS_LAYOUT, null);
590 itemResourceId = RESOURCE_MODE_ICONS_ITEM;
592 } else if (newMode.compareTo(NavigationLayoutMode.SIMPLE) == 0) {
593 newView = (AdapterView<ListAdapter>)inflate(
594 getContext(), RESOURCE_MODE_SIMPLE_LAYOUT, null);
595 itemResourceId = RESOURCE_MODE_SIMPLE_ITEM;
597 // Set the flinger listener (only when navigate)
598 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
599 if (useFlinger && newView instanceof FlingerListView) {
600 ((FlingerListView)newView).
601 setOnItemFlingerListener(this.mOnItemFlingerListener);
605 } else if (newMode.compareTo(NavigationLayoutMode.DETAILS) == 0) {
606 newView = (AdapterView<ListAdapter>)inflate(
607 getContext(), RESOURCE_MODE_DETAILS_LAYOUT, null);
608 itemResourceId = RESOURCE_MODE_DETAILS_ITEM;
610 // Set the flinger listener (only when navigate)
611 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
612 if (useFlinger && newView instanceof FlingerListView) {
613 ((FlingerListView)newView).
614 setOnItemFlingerListener(this.mOnItemFlingerListener);
619 //Get the current adapter and its adapter list
620 List<FileSystemObject> files = new ArrayList<FileSystemObject>(this.mFiles);
621 final AdapterView<ListAdapter> current =
622 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
623 FileSystemObjectAdapter adapter =
624 new FileSystemObjectAdapter(
626 new ArrayList<FileSystemObject>(),
628 this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0);
629 adapter.setOnSelectionChangedListener(this);
631 //Remove current layout
632 if (current != null) {
633 if (current.getAdapter() != null) {
634 //Save selected items before dispose adapter
635 FileSystemObjectAdapter currentAdapter =
636 ((FileSystemObjectAdapter)current.getAdapter());
637 adapter.setSelectedItems(currentAdapter.getSelectedItems());
638 currentAdapter.dispose();
643 adapter.addAll(files);
644 adapter.notifyDataSetChanged();
647 this.mAdapter = adapter;
648 newView.setAdapter(this.mAdapter);
649 newView.setOnItemClickListener(NavigationView.this);
652 this.mAdapterView = newView;
654 this.mCurrentMode = newMode;
656 // Pick mode doesn't implements the onlongclick
657 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
658 this.mAdapterView.setOnItemLongClickListener(this);
660 this.mAdapterView.setOnItemLongClickListener(null);
663 //Save the preference (only in navigation browse mode)
664 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
666 Preferences.savePreference(
667 FileManagerSettings.SETTINGS_LAYOUT_MODE, newMode, true);
668 } catch (Exception ex) {
669 Log.e(TAG, "Save of view mode preference fails", ex); //$NON-NLS-1$
676 * Method that removes a {@link FileSystemObject} from the view
678 * @param fso The file system object
680 public void removeItem(FileSystemObject fso) {
681 this.mAdapter.remove(fso);
682 // Delete also from internal list
684 int cc = this.mFiles.size()-1;
685 for (int i = cc; i >= 0; i--) {
686 FileSystemObject f = this.mFiles.get(i);
687 if (f != null && f.compareTo(fso) == 0) {
688 this.mFiles.remove(i);
693 this.mAdapter.notifyDataSetChanged();
697 * Method that removes a file system object from his path from the view
699 * @param path The file system object path
701 public void removeItem(String path) {
702 FileSystemObject fso = this.mAdapter.getItem(path);
704 this.mAdapter.remove(fso);
705 this.mAdapter.notifyDataSetChanged();
710 * Method that returns the current directory.
712 * @return String The current directory
714 public String getCurrentDir() {
715 return this.mCurrentDir;
719 * Method that changes the current directory of the view.
721 * @param newDir The new directory location
723 public void changeCurrentDir(final String newDir) {
724 changeCurrentDir(newDir, true, false, false, null, null);
728 * Method that changes the current directory of the view.
730 * @param newDir The new directory location
731 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
733 public void changeCurrentDir(final String newDir, SearchInfoParcelable searchInfo) {
734 changeCurrentDir(newDir, true, false, false, searchInfo, null);
738 * Method that changes the current directory of the view.
740 * @param newDir The new directory location
741 * @param addToHistory Add the directory to history
742 * @param reload Force the reload of the data
743 * @param useCurrent If this method must use the actual data (for back actions)
744 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
745 * @param scrollTo If not null, then listview must scroll to this item
747 private void changeCurrentDir(
748 final String newDir, final boolean addToHistory,
749 final boolean reload, final boolean useCurrent,
750 final SearchInfoParcelable searchInfo, final FileSystemObject scrollTo) {
752 // Check navigation security (don't allow to go outside the ChRooted environment if one
754 final String fNewDir = checkChRootedNavigation(newDir);
756 synchronized (this.mSync) {
757 //Check that it is really necessary change the directory
758 if (!reload && this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0) {
762 final boolean hasChanged =
763 !(this.mCurrentDir != null && this.mCurrentDir.compareTo(fNewDir) == 0);
764 final boolean isNewHistory = (this.mCurrentDir != null);
766 //Execute the listing in a background process
767 AsyncTask<String, Integer, List<FileSystemObject>> task =
768 new AsyncTask<String, Integer, List<FileSystemObject>>() {
773 protected List<FileSystemObject> doInBackground(String... params) {
775 //Reset the custom title view and returns to breadcrumb
776 if (NavigationView.this.mTitle != null) {
777 NavigationView.this.mTitle.post(new Runnable() {
781 NavigationView.this.mTitle.restoreView();
782 } catch (Exception e) {
790 //Start of loading data
791 if (NavigationView.this.mBreadcrumb != null) {
793 NavigationView.this.mBreadcrumb.startLoading();
794 } catch (Throwable ex) {
799 //Get the files, resolve links and apply configuration
800 //(sort, hidden, ...)
801 List<FileSystemObject> files = NavigationView.this.mFiles;
803 files = CommandHelper.listFiles(getContext(), fNewDir, null);
806 } catch (final ConsoleAllocException e) {
807 //Show exception and exists
808 NavigationView.this.post(new Runnable() {
811 Context ctx = getContext();
812 Log.e(TAG, ctx.getString(
813 R.string.msgs_cant_create_console), e);
814 DialogHelper.showToast(ctx,
815 R.string.msgs_cant_create_console,
817 ((Activity)ctx).finish();
822 } catch (Exception ex) {
823 //End of loading data
824 if (NavigationView.this.mBreadcrumb != null) {
826 NavigationView.this.mBreadcrumb.endLoading();
827 } catch (Throwable ex2) {
832 //Capture exception (attach task, and use listener to do the anim)
833 ExceptionUtil.attachAsyncTask(
835 new AsyncTask<Object, Integer, Boolean>() {
837 @SuppressWarnings("unchecked")
838 protected Boolean doInBackground(Object... taskParams) {
839 final List<FileSystemObject> files =
840 (List<FileSystemObject>)taskParams[0];
841 NavigationView.this.mAdapterView.post(
847 isNewHistory, hasChanged,
848 searchInfo, fNewDir, scrollTo);
854 final OnRelaunchCommandResult exListener =
855 new OnRelaunchCommandResult() {
857 public void onSuccess() {
862 public void onFailed(Throwable cause) {
867 public void onCancelled() {
872 ExceptionUtil.translateException(
873 getContext(), ex, false, true, exListener);
882 protected void onPreExecute() {
893 protected void onPostExecute(List<FileSystemObject> files) {
896 files, addToHistory, isNewHistory,
897 hasChanged, searchInfo, fNewDir, scrollTo);
905 * Method that performs a fade animation.
907 * @param out Fade out (true); Fade in (false)
909 void fadeEfect(boolean out) {
910 Animation fadeAnim = out ?
911 new AlphaAnimation(1, 0) :
912 new AlphaAnimation(0, 1);
913 fadeAnim.setDuration(50L);
914 fadeAnim.setFillAfter(true);
915 fadeAnim.setInterpolator(new AccelerateInterpolator());
916 NavigationView.this.startAnimation(fadeAnim);
919 task.execute(fNewDir);
925 * Method invoked when a execution ends.
927 * @param files The files obtains from the list
928 * @param addToHistory If add path to history
929 * @param isNewHistory If is new history
930 * @param hasChanged If current directory was changed
931 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
932 * @param newDir The new directory
933 * @param scrollTo If not null, then listview must scroll to this item
936 void onPostExecuteTask(
937 List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
938 boolean hasChanged, SearchInfoParcelable searchInfo,
939 String newDir, final FileSystemObject scrollTo) {
941 //Check that there is not errors and have some data
946 //Apply user preferences
947 List<FileSystemObject> sortedFiles =
948 FileHelper.applyUserPreferences(files, this.mRestrictions, this.mChRooted);
950 //Remove parent directory if we are in the root of a chrooted environment
951 if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
952 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
958 loadData(sortedFiles);
959 this.mFiles = sortedFiles;
960 if (searchInfo != null) {
961 searchInfo.setSuccessNavigation(true);
965 if (addToHistory && hasChanged && isNewHistory) {
966 if (this.mOnHistoryListener != null) {
967 //Communicate the need of a history change
968 this.mOnHistoryListener.onNewHistory(onSaveState());
972 //Change the breadcrumb
973 if (this.mBreadcrumb != null) {
974 this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
978 if (scrollTo != null) {
982 //The current directory is now the "newDir"
983 this.mCurrentDir = newDir;
986 //If calling activity is search, then save the search history
987 if (searchInfo != null) {
988 this.mOnHistoryListener.onNewHistory(searchInfo);
991 //End of loading data
993 NavigationView.this.mBreadcrumb.endLoading();
994 } catch (Throwable ex) {
1001 * Method that loads the files in the adapter.
1003 * @param files The files to load in the adapter
1006 @SuppressWarnings("unchecked")
1007 private void loadData(final List<FileSystemObject> files) {
1008 //Notify data to adapter view
1009 final AdapterView<ListAdapter> view =
1010 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
1011 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
1013 adapter.addAll(files);
1014 adapter.notifyDataSetChanged();
1015 view.setSelection(0);
1022 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1023 // Different actions depending on user preference
1025 // Get the adapter and the fso
1026 FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
1027 FileSystemObject fso = adapter.getItem(position);
1029 // Parent directory hasn't actions
1030 if (fso instanceof ParentDirectory) {
1034 // Pick mode doesn't implements the onlongclick
1035 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
1040 return true; //Always consume the event
1044 * Method that opens or navigates to the {@link FileSystemObject}
1046 * @param fso The file system object
1048 public void open(FileSystemObject fso) {
1053 * Method that opens or navigates to the {@link FileSystemObject}
1055 * @param fso The file system object
1056 * @param searchInfo The search info
1058 public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
1059 // If is a folder, then navigate to
1060 if (FileHelper.isDirectory(fso)) {
1061 changeCurrentDir(fso.getFullPath(), searchInfo);
1063 // Open the file with the preferred registered app
1064 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1072 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1074 FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
1075 if (fso instanceof ParentDirectory) {
1076 changeCurrentDir(fso.getParent(), true, false, false, null, null);
1078 } else if (fso instanceof Directory) {
1079 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
1081 } else if (fso instanceof Symlink) {
1082 Symlink symlink = (Symlink)fso;
1083 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
1085 symlink.getLinkRef().getFullPath(), true, false, false, null, null);
1089 // Open the link ref
1090 fso = symlink.getLinkRef();
1093 // Open the file (edit or pick)
1094 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
1095 // Open the file with the preferred registered app
1096 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1098 // Request a file pick selection
1099 if (this.mOnFilePickedListener != null) {
1100 this.mOnFilePickedListener.onFilePicked(fso);
1103 } catch (Throwable ex) {
1104 ExceptionUtil.translateException(getContext(), ex);
1112 public void onRequestRefresh(Object o, boolean clearSelection) {
1113 if (o instanceof FileSystemObject) {
1114 refresh((FileSystemObject)o);
1115 } else if (o == null) {
1118 if (clearSelection) {
1127 public void onRequestRemove(Object o, boolean clearSelection) {
1128 if (o != null && o instanceof FileSystemObject) {
1129 removeItem((FileSystemObject)o);
1131 onRequestRefresh(null, clearSelection);
1133 if (clearSelection) {
1142 public void onNavigateTo(Object o) {
1150 public void onBreadcrumbItemClick(BreadcrumbItem item) {
1151 changeCurrentDir(item.getItemPath(), true, true, false, null, null);
1158 public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
1159 if (this.mOnNavigationSelectionChangedListener != null) {
1160 this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
1165 * Method invoked when a request to show the menu associated
1166 * with an item is started.
1168 * @param item The item for which the request was started
1170 public void onRequestMenu(final FileSystemObject item) {
1171 if (this.mOnNavigationRequestMenuListener != null) {
1172 this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
1180 public void onToggleSelection(FileSystemObject fso) {
1181 if (this.mAdapter != null) {
1182 this.mAdapter.toggleSelection(fso);
1190 public void onDeselectAll() {
1191 if (this.mAdapter != null) {
1192 this.mAdapter.deselectedAll();
1200 public void onSelectAllVisibleItems() {
1201 if (this.mAdapter != null) {
1202 this.mAdapter.selectedAllVisibleItems();
1210 public void onDeselectAllVisibleItems() {
1211 if (this.mAdapter != null) {
1212 this.mAdapter.deselectedAllVisibleItems();
1220 public List<FileSystemObject> onRequestSelectedFiles() {
1221 return this.getSelectedFiles();
1228 public List<FileSystemObject> onRequestCurrentItems() {
1229 return this.getFiles();
1236 public String onRequestCurrentDir() {
1237 return this.mCurrentDir;
1241 * Method that creates a ChRooted environment, protecting the user to break anything
1245 public void createChRooted() {
1246 // If we are in a ChRooted environment, then do nothing
1247 if (this.mChRooted) return;
1248 this.mChRooted = true;
1250 //Change to first storage volume
1251 StorageVolume[] volumes =
1252 StorageHelper.getStorageVolumes(getContext());
1253 if (volumes != null && volumes.length > 0) {
1254 changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1259 * Method that exits from a ChRooted environment
1262 public void exitChRooted() {
1263 // If we aren't in a ChRooted environment, then do nothing
1264 if (!this.mChRooted) return;
1265 this.mChRooted = false;
1272 * Method that ensures that the user don't go outside the ChRooted environment
1274 * @param newDir The new directory to navigate to
1277 private String checkChRootedNavigation(String newDir) {
1278 // If we aren't in ChRooted environment, then there is nothing to check
1279 if (!this.mChRooted) return newDir;
1281 // Check if the path is owned by one of the storage volumes
1282 if (!StorageHelper.isPathInStorageVolume(newDir)) {
1283 StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext());
1284 if (volumes != null && volumes.length > 0) {
1285 return volumes[0].getPath();
1292 * Method that applies the current theme to the activity
1294 public void applyTheme() {
1296 if (getBreadcrumb() != null) {
1297 getBreadcrumb().applyTheme();
1300 //- Redraw the adapter view
1301 Theme theme = ThemeManager.getCurrentTheme(getContext());
1302 theme.setBackgroundDrawable(getContext(), this, "background_drawable"); //$NON-NLS-1$
1303 if (this.mAdapter != null) {
1304 this.mAdapter.notifyThemeChanged();
1306 if (this.mAdapterView instanceof ListView) {
1307 ((ListView)this.mAdapterView).setDivider(
1308 theme.getDrawable(getContext(), "horizontal_divider_drawable")); //$NON-NLS-1$