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>() {
836 private List<FileSystemObject> mTaskFiles = null;
839 "unchecked", "unqualified-field-access"
841 protected Boolean doInBackground(Object... taskParams) {
842 mTaskFiles = (List<FileSystemObject>)taskParams[0];
847 @SuppressWarnings("unqualified-field-access")
848 protected void onPostExecute(Boolean result) {
849 if (!result.booleanValue()){
853 mTaskFiles, addToHistory,
854 isNewHistory, hasChanged,
855 searchInfo, fNewDir, scrollTo);
858 final OnRelaunchCommandResult exListener =
859 new OnRelaunchCommandResult() {
861 public void onSuccess() {
866 public void onFailed(Throwable cause) {
871 public void onCancelled() {
876 ExceptionUtil.translateException(
877 getContext(), ex, false, true, exListener);
886 protected void onPreExecute() {
897 protected void onPostExecute(List<FileSystemObject> files) {
900 files, addToHistory, isNewHistory,
901 hasChanged, searchInfo, fNewDir, scrollTo);
909 * Method that performs a fade animation.
911 * @param out Fade out (true); Fade in (false)
913 void fadeEfect(final boolean out) {
914 Activity activity = (Activity)getContext();
915 activity.runOnUiThread(new Runnable() {
918 Animation fadeAnim = out ?
919 new AlphaAnimation(1, 0) :
920 new AlphaAnimation(0, 1);
921 fadeAnim.setDuration(50L);
922 fadeAnim.setFillAfter(true);
923 fadeAnim.setInterpolator(new AccelerateInterpolator());
924 NavigationView.this.startAnimation(fadeAnim);
929 task.execute(fNewDir);
935 * Method invoked when a execution ends.
937 * @param files The files obtains from the list
938 * @param addToHistory If add path to history
939 * @param isNewHistory If is new history
940 * @param hasChanged If current directory was changed
941 * @param searchInfo The search information (if calling activity is {@link "SearchActivity"})
942 * @param newDir The new directory
943 * @param scrollTo If not null, then listview must scroll to this item
946 void onPostExecuteTask(
947 List<FileSystemObject> files, boolean addToHistory, boolean isNewHistory,
948 boolean hasChanged, SearchInfoParcelable searchInfo,
949 String newDir, final FileSystemObject scrollTo) {
951 //Check that there is not errors and have some data
956 //Apply user preferences
957 List<FileSystemObject> sortedFiles =
958 FileHelper.applyUserPreferences(files, this.mRestrictions, this.mChRooted);
960 //Remove parent directory if we are in the root of a chrooted environment
961 if (this.mChRooted && StorageHelper.isStorageVolume(newDir)) {
962 if (files.size() > 0 && files.get(0) instanceof ParentDirectory) {
968 loadData(sortedFiles);
969 this.mFiles = sortedFiles;
970 if (searchInfo != null) {
971 searchInfo.setSuccessNavigation(true);
975 if (addToHistory && hasChanged && isNewHistory) {
976 if (this.mOnHistoryListener != null) {
977 //Communicate the need of a history change
978 this.mOnHistoryListener.onNewHistory(onSaveState());
982 //Change the breadcrumb
983 if (this.mBreadcrumb != null) {
984 this.mBreadcrumb.changeBreadcrumbPath(newDir, this.mChRooted);
988 if (scrollTo != null) {
992 //The current directory is now the "newDir"
993 this.mCurrentDir = newDir;
996 //If calling activity is search, then save the search history
997 if (searchInfo != null) {
998 this.mOnHistoryListener.onNewHistory(searchInfo);
1001 //End of loading data
1003 NavigationView.this.mBreadcrumb.endLoading();
1004 } catch (Throwable ex) {
1011 * Method that loads the files in the adapter.
1013 * @param files The files to load in the adapter
1016 @SuppressWarnings("unchecked")
1017 private void loadData(final List<FileSystemObject> files) {
1018 //Notify data to adapter view
1019 final AdapterView<ListAdapter> view =
1020 (AdapterView<ListAdapter>)findViewById(RESOURCE_CURRENT_LAYOUT);
1021 FileSystemObjectAdapter adapter = (FileSystemObjectAdapter)view.getAdapter();
1023 adapter.addAll(files);
1024 adapter.notifyDataSetChanged();
1025 view.setSelection(0);
1032 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
1033 // Different actions depending on user preference
1035 // Get the adapter and the fso
1036 FileSystemObjectAdapter adapter = ((FileSystemObjectAdapter)parent.getAdapter());
1037 FileSystemObject fso = adapter.getItem(position);
1039 // Parent directory hasn't actions
1040 if (fso instanceof ParentDirectory) {
1044 // Pick mode doesn't implements the onlongclick
1045 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.PICKABLE) == 0) {
1050 return true; //Always consume the event
1054 * Method that opens or navigates to the {@link FileSystemObject}
1056 * @param fso The file system object
1058 public void open(FileSystemObject fso) {
1063 * Method that opens or navigates to the {@link FileSystemObject}
1065 * @param fso The file system object
1066 * @param searchInfo The search info
1068 public void open(FileSystemObject fso, SearchInfoParcelable searchInfo) {
1069 // If is a folder, then navigate to
1070 if (FileHelper.isDirectory(fso)) {
1071 changeCurrentDir(fso.getFullPath(), searchInfo);
1073 // Open the file with the preferred registered app
1074 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1082 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1084 FileSystemObject fso = ((FileSystemObjectAdapter)parent.getAdapter()).getItem(position);
1085 if (fso instanceof ParentDirectory) {
1086 changeCurrentDir(fso.getParent(), true, false, false, null, null);
1088 } else if (fso instanceof Directory) {
1089 changeCurrentDir(fso.getFullPath(), true, false, false, null, null);
1091 } else if (fso instanceof Symlink) {
1092 Symlink symlink = (Symlink)fso;
1093 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
1095 symlink.getLinkRef().getFullPath(), true, false, false, null, null);
1099 // Open the link ref
1100 fso = symlink.getLinkRef();
1103 // Open the file (edit or pick)
1104 if (this.mNavigationMode.compareTo(NAVIGATION_MODE.BROWSABLE) == 0) {
1105 // Open the file with the preferred registered app
1106 IntentsActionPolicy.openFileSystemObject(getContext(), fso, false, null, null);
1108 // Request a file pick selection
1109 if (this.mOnFilePickedListener != null) {
1110 this.mOnFilePickedListener.onFilePicked(fso);
1113 } catch (Throwable ex) {
1114 ExceptionUtil.translateException(getContext(), ex);
1122 public void onRequestRefresh(Object o, boolean clearSelection) {
1123 if (o instanceof FileSystemObject) {
1124 refresh((FileSystemObject)o);
1125 } else if (o == null) {
1128 if (clearSelection) {
1137 public void onRequestRemove(Object o, boolean clearSelection) {
1138 if (o != null && o instanceof FileSystemObject) {
1139 removeItem((FileSystemObject)o);
1141 onRequestRefresh(null, clearSelection);
1143 if (clearSelection) {
1152 public void onNavigateTo(Object o) {
1160 public void onBreadcrumbItemClick(BreadcrumbItem item) {
1161 changeCurrentDir(item.getItemPath(), true, true, false, null, null);
1168 public void onSelectionChanged(final List<FileSystemObject> selectedItems) {
1169 if (this.mOnNavigationSelectionChangedListener != null) {
1170 this.mOnNavigationSelectionChangedListener.onSelectionChanged(this, selectedItems);
1175 * Method invoked when a request to show the menu associated
1176 * with an item is started.
1178 * @param item The item for which the request was started
1180 public void onRequestMenu(final FileSystemObject item) {
1181 if (this.mOnNavigationRequestMenuListener != null) {
1182 this.mOnNavigationRequestMenuListener.onRequestMenu(this, item);
1190 public void onToggleSelection(FileSystemObject fso) {
1191 if (this.mAdapter != null) {
1192 this.mAdapter.toggleSelection(fso);
1200 public void onDeselectAll() {
1201 if (this.mAdapter != null) {
1202 this.mAdapter.deselectedAll();
1210 public void onSelectAllVisibleItems() {
1211 if (this.mAdapter != null) {
1212 this.mAdapter.selectedAllVisibleItems();
1220 public void onDeselectAllVisibleItems() {
1221 if (this.mAdapter != null) {
1222 this.mAdapter.deselectedAllVisibleItems();
1230 public List<FileSystemObject> onRequestSelectedFiles() {
1231 return this.getSelectedFiles();
1238 public List<FileSystemObject> onRequestCurrentItems() {
1239 return this.getFiles();
1246 public String onRequestCurrentDir() {
1247 return this.mCurrentDir;
1251 * Method that creates a ChRooted environment, protecting the user to break anything
1255 public void createChRooted() {
1256 // If we are in a ChRooted environment, then do nothing
1257 if (this.mChRooted) return;
1258 this.mChRooted = true;
1260 //Change to first storage volume
1261 StorageVolume[] volumes =
1262 StorageHelper.getStorageVolumes(getContext());
1263 if (volumes != null && volumes.length > 0) {
1264 changeCurrentDir(volumes[0].getPath(), false, true, false, null, null);
1269 * Method that exits from a ChRooted environment
1272 public void exitChRooted() {
1273 // If we aren't in a ChRooted environment, then do nothing
1274 if (!this.mChRooted) return;
1275 this.mChRooted = false;
1282 * Method that ensures that the user don't go outside the ChRooted environment
1284 * @param newDir The new directory to navigate to
1287 private String checkChRootedNavigation(String newDir) {
1288 // If we aren't in ChRooted environment, then there is nothing to check
1289 if (!this.mChRooted) return newDir;
1291 // Check if the path is owned by one of the storage volumes
1292 if (!StorageHelper.isPathInStorageVolume(newDir)) {
1293 StorageVolume[] volumes = StorageHelper.getStorageVolumes(getContext());
1294 if (volumes != null && volumes.length > 0) {
1295 return volumes[0].getPath();
1302 * Method that applies the current theme to the activity
1304 public void applyTheme() {
1306 if (getBreadcrumb() != null) {
1307 getBreadcrumb().applyTheme();
1310 //- Redraw the adapter view
1311 Theme theme = ThemeManager.getCurrentTheme(getContext());
1312 theme.setBackgroundDrawable(getContext(), this, "background_drawable"); //$NON-NLS-1$
1313 if (this.mAdapter != null) {
1314 this.mAdapter.notifyThemeChanged();
1316 if (this.mAdapterView instanceof ListView) {
1317 ((ListView)this.mAdapterView).setDivider(
1318 theme.getDrawable(getContext(), "horizontal_divider_drawable")); //$NON-NLS-1$