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.activities;
19 import android.app.ActionBar;
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.app.SearchManager;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.ActivityInfo;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.Parcelable;
32 import android.os.storage.StorageVolume;
33 import android.util.Log;
34 import android.view.KeyEvent;
35 import android.view.Menu;
36 import android.view.MenuItem;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.widget.AdapterView;
40 import android.widget.AdapterView.OnItemClickListener;
41 import android.widget.ListPopupWindow;
42 import android.widget.PopupWindow;
43 import android.widget.Toast;
45 import com.cyanogenmod.filemanager.FileManagerApplication;
46 import com.cyanogenmod.filemanager.R;
47 import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences;
48 import com.cyanogenmod.filemanager.adapters.HighlightedSimpleMenuListAdapter;
49 import com.cyanogenmod.filemanager.adapters.MenuSettingsAdapter;
50 import com.cyanogenmod.filemanager.adapters.SimpleMenuListAdapter;
51 import com.cyanogenmod.filemanager.console.Console;
52 import com.cyanogenmod.filemanager.console.ConsoleAllocException;
53 import com.cyanogenmod.filemanager.console.ConsoleBuilder;
54 import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
55 import com.cyanogenmod.filemanager.listeners.OnHistoryListener;
56 import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
57 import com.cyanogenmod.filemanager.model.DiskUsage;
58 import com.cyanogenmod.filemanager.model.FileSystemObject;
59 import com.cyanogenmod.filemanager.model.History;
60 import com.cyanogenmod.filemanager.model.MountPoint;
61 import com.cyanogenmod.filemanager.parcelables.HistoryNavigable;
62 import com.cyanogenmod.filemanager.parcelables.NavigationViewInfoParcelable;
63 import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
64 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
65 import com.cyanogenmod.filemanager.preferences.NavigationLayoutMode;
66 import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
67 import com.cyanogenmod.filemanager.preferences.Preferences;
68 import com.cyanogenmod.filemanager.ui.dialogs.ActionsDialog;
69 import com.cyanogenmod.filemanager.ui.dialogs.FilesystemInfoDialog;
70 import com.cyanogenmod.filemanager.ui.dialogs.FilesystemInfoDialog.OnMountListener;
71 import com.cyanogenmod.filemanager.ui.widgets.Breadcrumb;
72 import com.cyanogenmod.filemanager.ui.widgets.NavigationCustomTitleView;
73 import com.cyanogenmod.filemanager.ui.widgets.NavigationView;
74 import com.cyanogenmod.filemanager.ui.widgets.NavigationView.OnNavigationRequestMenuListener;
75 import com.cyanogenmod.filemanager.ui.widgets.NavigationView.OnNavigationSelectionChangedListener;
76 import com.cyanogenmod.filemanager.ui.widgets.SelectionView;
77 import com.cyanogenmod.filemanager.util.AndroidHelper;
78 import com.cyanogenmod.filemanager.util.CommandHelper;
79 import com.cyanogenmod.filemanager.util.DialogHelper;
80 import com.cyanogenmod.filemanager.util.ExceptionUtil;
81 import com.cyanogenmod.filemanager.util.FileHelper;
82 import com.cyanogenmod.filemanager.util.StorageHelper;
84 import java.io.FileNotFoundException;
85 import java.io.Serializable;
86 import java.util.ArrayList;
87 import java.util.Arrays;
88 import java.util.List;
91 * The main navigation activity. This activity is the center of the application.
92 * From this the user can navigate, search, make actions.<br/>
93 * This activity is singleTop, so when it is displayed no other activities exists in
95 * This cause an issue with the saved instance of this class, because if another activity
96 * is displayed, and the process is killed, NavigationActivity is started and the saved
97 * instance gets corrupted.<br/>
98 * For this reason the methods {link {@link Activity#onSaveInstanceState(Bundle)} and
99 * {@link Activity#onRestoreInstanceState(Bundle)} are not implemented, and every time
100 * the app is killed, is restarted from his initial state.
102 public class NavigationActivity extends Activity
103 implements OnHistoryListener, OnRequestRefreshListener,
104 OnNavigationRequestMenuListener, OnNavigationSelectionChangedListener {
106 private static final String TAG = "NavigationActivity"; //$NON-NLS-1$
108 private static boolean DEBUG = false;
111 * Intent code for request a bookmark selection.
113 public static final int INTENT_REQUEST_BOOKMARK = 10001;
116 * Intent code for request a history selection.
118 public static final int INTENT_REQUEST_HISTORY = 20001;
121 * Intent code for request a search.
123 public static final int INTENT_REQUEST_SEARCH = 30001;
127 * Constant for extra information about selected bookmark.
129 public static final String EXTRA_BOOKMARK_SELECTION =
130 "extra_bookmark_selection"; //$NON-NLS-1$
133 * Constant for extra information about selected history entry.
135 public static final String EXTRA_HISTORY_ENTRY_SELECTION =
136 "extra_history_entry_selection"; //$NON-NLS-1$
139 * Constant for extra information about clear selection action.
141 public static final String EXTRA_HISTORY_CLEAR =
142 "extra_history_clear_history"; //$NON-NLS-1$
145 * Constant for extra information about selected search entry.
147 public static final String EXTRA_SEARCH_ENTRY_SELECTION =
148 "extra_search_entry_selection"; //$NON-NLS-1$
151 * Constant for extra information about last search data.
153 public static final String EXTRA_SEARCH_LAST_SEARCH_DATA =
154 "extra_search_last_search_data"; //$NON-NLS-1$
157 * Constant for extra information for request a navigation to the passed path.
159 public static final String EXTRA_NAVIGATE_TO =
160 "extra_navigate_to"; //$NON-NLS-1$
162 // The timeout needed to reset the exit status for back button
163 // After this time user need to tap 2 times the back button to
164 // exit, and the toast is shown again after the first tap.
165 private static final int RELEASE_EXIT_CHECK_TIMEOUT = 3500;
167 private final BroadcastReceiver mOnSettingChangeReceiver = new BroadcastReceiver() {
169 public void onReceive(Context context, Intent intent) {
170 if (intent != null) {
171 if (intent.getAction().compareTo(FileManagerSettings.INTENT_SETTING_CHANGED) == 0) {
172 // The settings has changed
173 String key = intent.getStringExtra(FileManagerSettings.EXTRA_SETTING_CHANGED_KEY);
175 // Disk usage warning level
176 if (key.compareTo(FileManagerSettings.
177 SETTINGS_DISK_USAGE_WARNING_LEVEL.getId()) == 0) {
179 // Set the free disk space warning level of the breadcrumb widget
180 Breadcrumb breadcrumb = getCurrentNavigationView().getBreadcrumb();
181 String fds = Preferences.getSharedPreferences().getString(
182 FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
183 (String)FileManagerSettings.
184 SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
185 breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
186 breadcrumb.updateMountPointInfo();
190 // Case sensitive sort
191 if (key.compareTo(FileManagerSettings.
192 SETTINGS_CASE_SENSITIVE_SORT.getId()) == 0) {
193 getCurrentNavigationView().refresh();
198 if (key.compareTo(FileManagerSettings.
199 SETTINGS_ADVANCE_MODE.getId()) == 0) {
200 // Is it necessary to create or exit of the ChRooted?
201 boolean chRooted = !FileManagerApplication.isAdvancedMode();
202 if (chRooted != NavigationActivity.this.mChRooted) {
212 } else if (intent.getAction().compareTo(
213 FileManagerSettings.INTENT_FILE_CHANGED) == 0) {
214 // Retrieve the file that was changed
216 intent.getStringExtra(FileManagerSettings.EXTRA_FILE_CHANGED_KEY);
218 FileSystemObject fso = CommandHelper.getFileInfo(context, file, null);
220 getCurrentNavigationView().refresh(fso);
222 } catch (Exception e) {
223 ExceptionUtil.translateException(context, e, true, false);
233 NavigationView[] mNavigationViews;
234 private List<History> mHistory;
236 private int mCurrentNavigationView;
238 private ViewGroup mActionBar;
239 private SelectionView mSelectionBar;
241 private boolean mExitFlag = false;
242 private long mExitBackTimeout = -1;
244 private View mOptionsAnchorView;
260 protected void onCreate(Bundle state) {
263 Log.d(TAG, "NavigationActivity.onCreate"); //$NON-NLS-1$
266 // Register the broadcast receiver
267 IntentFilter filter = new IntentFilter();
268 filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED);
269 filter.addAction(FileManagerSettings.INTENT_FILE_CHANGED);
270 registerReceiver(this.mOnSettingChangeReceiver, filter);
273 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
275 //Set the main layout of the activity
276 setContentView(R.layout.navigation);
278 // Show welcome message
281 //Initialize activity
285 initNavigationViews();
287 //Initialize action bars
288 initTitleActionBar();
289 initStatusActionBar();
292 this.mHandler = new Handler();
293 this.mHandler.post(new Runnable() {
296 //Initialize navigation
297 int cc = NavigationActivity.this.mNavigationViews.length;
298 for (int i = 0; i < cc; i++) {
299 initNavigation(i, false);
302 //Check the intent action
303 checkIntent(getIntent());
308 super.onCreate(state);
315 protected void onNewIntent(Intent intent) {
316 //Initialize navigation
317 initNavigation(this.mCurrentNavigationView, true);
319 //Check the intent action
327 protected void onDestroy() {
329 Log.d(TAG, "NavigationActivity.onDestroy"); //$NON-NLS-1$
332 // Unregister the receiver
334 unregisterReceiver(this.mOnSettingChangeReceiver);
335 } catch (Throwable ex) {
339 //All destroy. Continue
344 * Method that returns the current navigation view.
346 * @return NavigationView The current navigation view
348 public NavigationView getCurrentNavigationView() {
349 return getNavigationView(this.mCurrentNavigationView);
353 * Method that returns the current navigation view.
355 * @param viewId The view to return
356 * @return NavigationView The current navigation view
358 public NavigationView getNavigationView(int viewId) {
359 return this.mNavigationViews[viewId];
363 * Method that initializes the activity.
365 private void init() {
366 this.mHistory = new ArrayList<History>();
367 this.mChRooted = !FileManagerApplication.isAdvancedMode();
371 * Method that displays a welcome message the first time the user
372 * access the application
374 private void showWelcomeMsg() {
375 boolean firstUse = Preferences.getSharedPreferences().getBoolean(
376 FileManagerSettings.SETTINGS_FIRST_USE.getId(),
377 ((Boolean)FileManagerSettings.SETTINGS_FIRST_USE.getDefaultValue()).booleanValue());
379 //Display the welcome message?
381 AlertDialog dialog = DialogHelper.createAlertDialog(
382 this, R.drawable.ic_launcher, R.string.welcome_title, R.string.welcome_msg);
385 // Don't display again this dialog
387 Preferences.savePreference(
388 FileManagerSettings.SETTINGS_FIRST_USE, Boolean.FALSE, true);
389 } catch (Exception e) {/**NON BLOCK**/}
394 * Method that initializes the titlebar of the activity.
396 private void initTitleActionBar() {
397 //Inflate the view and associate breadcrumb
398 NavigationCustomTitleView title =
399 (NavigationCustomTitleView)getLayoutInflater().inflate(
400 R.layout.navigation_view_customtitle, null, false);
401 title.setOnHistoryListener(this);
402 Breadcrumb breadcrumb = (Breadcrumb)title.findViewById(R.id.breadcrumb_view);
403 int cc = this.mNavigationViews.length;
404 for (int i = 0; i < cc; i++) {
405 this.mNavigationViews[i].setBreadcrumb(breadcrumb);
406 this.mNavigationViews[i].setOnHistoryListener(this);
407 this.mNavigationViews[i].setOnNavigationSelectionChangedListener(this);
408 this.mNavigationViews[i].setOnNavigationOnRequestMenuListener(this);
409 this.mNavigationViews[i].setCustomTitle(title);
412 // Set the free disk space warning level of the breadcrumb widget
413 String fds = Preferences.getSharedPreferences().getString(
414 FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
415 (String)FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
416 breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
418 //Configure the action bar options
419 getActionBar().setBackgroundDrawable(
420 getResources().getDrawable(R.drawable.bg_holo_titlebar));
421 getActionBar().setDisplayOptions(
422 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
423 getActionBar().setCustomView(title);
427 * Method that initializes the statusbar of the activity.
429 private void initStatusActionBar() {
430 //Performs a width calculation of buttons. Buttons exceeds the width
431 //of the action bar should be hidden
432 //This application not use android ActionBar because the application
433 //make uses of the title and bottom areas, and wants to force to show
434 //the overflow button (without care of physical buttons)
435 this.mActionBar = (ViewGroup)findViewById(R.id.navigation_actionbar);
436 this.mActionBar.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
438 public void onLayoutChange(
439 View v, int left, int top, int right, int bottom, int oldLeft,
440 int oldTop, int oldRight, int oldBottom) {
441 //Get the width of the action bar
442 int w = v.getMeasuredWidth();
444 //Wake through children calculation his dimensions
445 int bw = (int)getResources().getDimension(R.dimen.default_buttom_width);
447 final ViewGroup abView = ((ViewGroup)v);
448 int cc = abView.getChildCount();
449 for (int i = 0; i < cc; i++) {
450 View child = abView.getChildAt(i);
451 child.setVisibility(cw + bw > w ? View.INVISIBLE : View.VISIBLE);
457 // Have overflow menu?
458 View overflow = findViewById(R.id.ab_overflow);
459 boolean showOptionsMenu = AndroidHelper.showOptionsMenu(getApplicationContext());
460 overflow.setVisibility(showOptionsMenu ? View.VISIBLE : View.GONE);
461 this.mOptionsAnchorView = showOptionsMenu ? overflow : this.mActionBar;
465 * Method that initializes the selectionbar of the activity.
467 private void initSelectionBar() {
468 this.mSelectionBar = (SelectionView)findViewById(R.id.navigation_selectionbar);
472 * Method that initializes the navigation views of the activity
474 private void initNavigationViews() {
475 //Get the navigation views (wishlist: multiple view; for now only one view)
476 this.mNavigationViews = new NavigationView[1];
477 this.mCurrentNavigationView = 0;
479 this.mNavigationViews[0] = (NavigationView)findViewById(R.id.navigation_view);
480 this.mNavigationViews[0].setId(0);
484 * Method that initializes the navigation.
486 * @param viewId The navigation view identifier where apply the navigation
487 * @param restore Initialize from a restore info
490 void initNavigation(final int viewId, final boolean restore) {
491 final NavigationView navigationView = getNavigationView(viewId);
492 this.mHandler.post(new Runnable() {
495 //Create the default console (from the preferences)
497 Console console = ConsoleBuilder.getConsole(NavigationActivity.this);
498 if (console == null) {
499 throw new ConsoleAllocException("console == null"); //$NON-NLS-1$
501 } catch (Throwable ex) {
502 if (!NavigationActivity.this.mChRooted) {
503 //Show exception and exists
504 Log.e(TAG, getString(R.string.msgs_cant_create_console), ex);
505 // We don't have any console
506 // Show exception and exists
507 DialogHelper.showToast(
508 NavigationActivity.this,
509 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
514 // We are in a trouble (something is not allowing creating the console)
515 // Ask the user to return to advanced console mode with a
516 // non-privileged console, prior to make crash the application
521 //Is necessary navigate?
523 //Load the preference initial directory
525 Preferences.getSharedPreferences().getString(
526 FileManagerSettings.SETTINGS_INITIAL_DIR.getId(),
527 (String)FileManagerSettings.SETTINGS_INITIAL_DIR.getDefaultValue());
528 if (NavigationActivity.this.mChRooted) {
529 // Initial directory is the first external sdcard (sdcard, emmc, usb, ...)
530 StorageVolume[] volumes =
531 StorageHelper.getStorageVolumes(NavigationActivity.this);
532 if (volumes != null && volumes.length > 0) {
533 initialDir = volumes[0].getPath();
537 //Ensure initial is an absolute directory
540 CommandHelper.getAbsolutePath(
541 NavigationActivity.this, initialDir, null);
542 } catch (Throwable e) {
543 Log.e(TAG, "Resolve of initital directory fails", e); //$NON-NLS-1$
546 R.string.msgs_settings_invalid_initial_directory,
548 DialogHelper.showToast(NavigationActivity.this, msg, Toast.LENGTH_SHORT);
549 initialDir = FileHelper.ROOT_DIRECTORY;
552 // Change the current directory to the preference initial directory or the
554 String navigateTo = getIntent().getStringExtra(EXTRA_NAVIGATE_TO);
555 if (navigateTo != null && navigateTo.length() > 0) {
556 navigationView.changeCurrentDir(navigateTo);
558 navigationView.changeCurrentDir(initialDir);
566 * Method that verifies the intent passed to the activity, and checks
567 * if a request is made like Search.
569 * @param intent The intent to check
572 void checkIntent(Intent intent) {
574 if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
575 Intent searchIntent = new Intent(this, SearchActivity.class);
576 searchIntent.setAction(Intent.ACTION_SEARCH);
577 //- SearchActivity.EXTRA_SEARCH_DIRECTORY
578 searchIntent.putExtra(
579 SearchActivity.EXTRA_SEARCH_DIRECTORY,
580 getCurrentNavigationView().getCurrentDir());
581 //- SearchManager.APP_DATA
582 if (intent.getBundleExtra(SearchManager.APP_DATA) != null) {
583 Bundle bundle = new Bundle();
584 bundle.putAll(intent.getBundleExtra(SearchManager.APP_DATA));
585 searchIntent.putExtra(SearchManager.APP_DATA, bundle);
587 //-- SearchManager.QUERY
588 String query = intent.getStringExtra(SearchManager.QUERY);
590 searchIntent.putExtra(SearchManager.QUERY, query);
592 //- android.speech.RecognizerIntent.EXTRA_RESULTS
593 ArrayList<String> extraResults =
594 intent.getStringArrayListExtra(android.speech.RecognizerIntent.EXTRA_RESULTS);
595 if (extraResults != null) {
596 searchIntent.putStringArrayListExtra(
597 android.speech.RecognizerIntent.EXTRA_RESULTS, extraResults);
599 startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH);
603 // Navigate to the requested path
604 String navigateTo = intent.getStringExtra(EXTRA_NAVIGATE_TO);
605 if (navigateTo != null && navigateTo.length() >= 0) {
606 getCurrentNavigationView().changeCurrentDir(navigateTo);
615 public boolean onKeyUp(int keyCode, KeyEvent event) {
616 if (keyCode == KeyEvent.KEYCODE_MENU) {
617 showOverflowPopUp(this.mOptionsAnchorView);
620 if (keyCode == KeyEvent.KEYCODE_BACK) {
621 if (checkBackAction()) {
625 return super.onKeyUp(keyCode, event);
632 public boolean onOptionsItemSelected(MenuItem item) {
633 switch (item.getItemId()) {
634 case android.R.id.home:
635 if ((getActionBar().getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP)
636 == ActionBar.DISPLAY_HOME_AS_UP) {
641 return super.onOptionsItemSelected(item);
646 * Method invoked when an action item is clicked.
648 * @param view The button pushed
650 public void onActionBarItemClick(View view) {
651 switch (view.getId()) {
652 //######################
653 //Navigation Custom Title
654 //######################
655 case R.id.ab_configuration:
656 //Show navigation view configuration toolbar
657 getCurrentNavigationView().getCustomTitle().showConfigurationView();
658 getActionBar().setDisplayHomeAsUpEnabled(true);
659 getActionBar().setHomeButtonEnabled(true);
662 //Hide navigation view configuration toolbar
663 getCurrentNavigationView().getCustomTitle().hideConfigurationView();
666 //######################
668 //######################
669 case R.id.ab_filesystem_info:
670 //Show information of the filesystem
671 MountPoint mp = getCurrentNavigationView().getBreadcrumb().getMountPointInfo();
672 DiskUsage du = getCurrentNavigationView().getBreadcrumb().getDiskUsageInfo();
673 showMountPointInfo(mp, du);
676 //######################
677 //Navigation view options
678 //######################
679 case R.id.ab_sort_mode:
680 showSettingsPopUp(view,
681 Arrays.asList(new FileManagerSettings[]{FileManagerSettings.SETTINGS_SORT_MODE}));
683 case R.id.ab_layout_mode:
684 showSettingsPopUp(view,
686 new FileManagerSettings[]{FileManagerSettings.SETTINGS_LAYOUT_MODE}));
688 case R.id.ab_view_options:
689 // If we are in ChRooted mode, then don't show non-secure items
690 if (this.mChRooted) {
691 showSettingsPopUp(view,
692 Arrays.asList(new FileManagerSettings[]{
693 FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST}));
695 showSettingsPopUp(view,
696 Arrays.asList(new FileManagerSettings[]{
697 FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST,
698 FileManagerSettings.SETTINGS_SHOW_HIDDEN,
699 FileManagerSettings.SETTINGS_SHOW_SYSTEM,
700 FileManagerSettings.SETTINGS_SHOW_SYMLINKS}));
705 //######################
707 //######################
708 case R.id.ab_selection_done:
709 //Show information of the filesystem
710 getCurrentNavigationView().onDeselectAll();
713 //######################
715 //######################
716 case R.id.ab_actions:
717 openActionsDialog(getCurrentNavigationView().getCurrentDir(), true);
720 case R.id.ab_bookmarks:
724 case R.id.ab_history:
732 case R.id.ab_overflow:
733 showOverflowPopUp(view);
745 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
747 switch (requestCode) {
748 case INTENT_REQUEST_BOOKMARK:
749 if (resultCode == RESULT_OK) {
750 FileSystemObject fso =
751 (FileSystemObject)data.
752 getSerializableExtra(EXTRA_BOOKMARK_SELECTION);
755 getCurrentNavigationView().open(fso);
760 case INTENT_REQUEST_HISTORY:
761 if (resultCode == RESULT_OK) {
762 //Change current directory
764 (History)data.getSerializableExtra(EXTRA_HISTORY_ENTRY_SELECTION);
765 navigateToHistory(history);
766 } else if (resultCode == RESULT_CANCELED) {
767 boolean clear = data.getBooleanExtra(EXTRA_HISTORY_CLEAR, false);
774 case INTENT_REQUEST_SEARCH:
775 if (resultCode == RESULT_OK) {
777 FileSystemObject fso =
778 (FileSystemObject)data.
779 getSerializableExtra(EXTRA_SEARCH_ENTRY_SELECTION);
780 SearchInfoParcelable searchInfo =
781 data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
783 //Goto to new directory
784 getCurrentNavigationView().open(fso, searchInfo);
786 } else if (resultCode == RESULT_CANCELED) {
787 SearchInfoParcelable searchInfo =
788 data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
789 if (searchInfo != null && searchInfo.isSuccessNavigation()) {
790 //Navigate to previous history
793 // I don't know is the search view was changed, so do a refresh
794 // of the navigation view
795 getCurrentNavigationView().refresh();
810 public void onNewHistory(HistoryNavigable navigable) {
811 //Recollect information about current status
812 History history = new History(this.mHistory.size(), navigable);
813 this.mHistory.add(history);
814 getActionBar().setDisplayHomeAsUpEnabled(true);
815 getActionBar().setHomeButtonEnabled(true);
822 public void onCheckHistory() {
823 //Need to show HomeUp Button
824 boolean enabled = this.mHistory != null && this.mHistory.size() > 0;
825 getActionBar().setDisplayHomeAsUpEnabled(enabled);
826 getActionBar().setHomeButtonEnabled(enabled);
833 public void onRequestRefresh(Object o) {
834 if (o instanceof FileSystemObject) {
835 // Refresh only the item
836 this.getCurrentNavigationView().refresh((FileSystemObject)o);
837 } else if (o == null) {
839 getCurrentNavigationView().refresh();
841 this.getCurrentNavigationView().onDeselectAll();
848 public void onRequestRemove(Object o) {
849 if (o instanceof FileSystemObject) {
851 this.getCurrentNavigationView().removeItem((FileSystemObject)o);
853 //Remove from history
854 removeFromHistory((FileSystemObject)o);
856 this.getCurrentNavigationView().onDeselectAll();
863 public void onNavigateTo(Object o) {
871 public void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems) {
872 this.mSelectionBar.setSelection(selectedItems);
879 public void onRequestMenu(NavigationView navView, FileSystemObject item) {
880 // Show the actions dialog
881 openActionsDialog(item, false);
885 * Method that shows a popup with a menu associated a {@link FileManagerSettings}.
887 * @param anchor The action button that was pressed
888 * @param settings The array of settings associated with the action button
890 private void showSettingsPopUp(View anchor, List<FileManagerSettings> settings) {
892 final MenuSettingsAdapter adapter = new MenuSettingsAdapter(this, settings);
894 //Create a show the popup menu
895 final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor);
896 popup.setOnItemClickListener(new OnItemClickListener() {
898 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
899 FileManagerSettings setting =
900 ((MenuSettingsAdapter)parent.getAdapter()).getSetting(position);
901 final int value = ((MenuSettingsAdapter)parent.getAdapter()).getId(position);
904 if (setting.compareTo(FileManagerSettings.SETTINGS_LAYOUT_MODE) == 0) {
905 //Need to change the layout
906 getCurrentNavigationView().changeViewMode(
907 NavigationLayoutMode.fromId(value));
910 if (setting.getDefaultValue() instanceof Enum<?>) {
912 Preferences.savePreference(setting, new ObjectIdentifier() {
921 Preferences.getSharedPreferences().
924 ((Boolean)setting.getDefaultValue()).booleanValue());
925 Preferences.savePreference(setting, Boolean.valueOf(!newval), false);
927 getCurrentNavigationView().refresh();
929 } catch (Exception e) {
930 Log.e(TAG, "Error applying navigation option", e); //$NON-NLS-1$
931 NavigationActivity.this.mHandler.post(new Runnable() {
934 DialogHelper.showToast(
935 NavigationActivity.this,
936 R.string.msgs_settings_save_failure, Toast.LENGTH_SHORT);
942 getCurrentNavigationView().getCustomTitle().restoreView();
947 popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
949 public void onDismiss() {
957 * Method that shows a popup with the activity main menu.
959 * @param anchor The action button that was pressed
961 private void showOverflowPopUp(View anchor) {
962 SimpleMenuListAdapter adapter =
963 new HighlightedSimpleMenuListAdapter(this, R.menu.navigation);
964 Menu menu = adapter.getMenu();
965 int cc = this.mActionBar.getChildCount();
966 for (int i = 0, j = this.mActionBar.getChildCount() - 1; i < cc; i++, j--) {
967 View child = this.mActionBar.getChildAt(i);
968 boolean visible = child.getVisibility() == View.VISIBLE;
970 menu.removeItem(menu.getItem(j).getItemId());
974 final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor);
975 popup.setOnItemClickListener(new OnItemClickListener() {
977 public void onItemClick(
978 final AdapterView<?> parent, final View v, final int position, final long id) {
980 final int itemId = (int)id;
981 NavigationActivity.this.mHandler.post(new Runnable() {
986 case R.id.mnu_settings:
988 Intent settings = new Intent(
989 NavigationActivity.this, SettingsPreferences.class);
990 startActivity(settings);
993 case R.id.mnu_history:
999 case R.id.mnu_bookmarks:
1005 case R.id.mnu_search:
1021 * Method that show the information of a filesystem mount point.
1023 * @param mp The mount point info
1024 * @param du The disk usage of the mount point
1026 private void showMountPointInfo(MountPoint mp, DiskUsage du) {
1027 //Has mount point info?
1029 //There is no information
1031 DialogHelper.createWarningDialog(this, R.string.filesystem_info_warning_msg);
1036 //Show a the filesystem info dialog
1037 FilesystemInfoDialog dialog = new FilesystemInfoDialog(this, mp, du);
1038 dialog.setOnMountListener(new OnMountListener() {
1040 public void onRemount(MountPoint mountPoint) {
1041 //Update the statistics of breadcrumb, only if mount point is the same
1042 Breadcrumb breadcrumb = getCurrentNavigationView().getBreadcrumb();
1043 if (breadcrumb.getMountPointInfo().compareTo(mountPoint) == 0) {
1044 breadcrumb.updateMountPointInfo();
1052 * Method that checks the action that must be realized when the
1053 * back button is pushed.
1055 * @return boolean Indicates if the action must be intercepted
1057 private boolean checkBackAction() {
1058 //Check if the configuration view is showing. In this case back
1059 //action must be "close configuration"
1060 if (getCurrentNavigationView().getCustomTitle().isConfigurationViewShowing()) {
1061 getCurrentNavigationView().getCustomTitle().restoreView();
1065 //Do back operation over the navigation history
1066 boolean flag = this.mExitFlag;
1068 this.mExitFlag = !back();
1070 // Retrieve if the exit status timeout has expired
1071 long now = System.currentTimeMillis();
1072 boolean timeout = (this.mExitBackTimeout == -1 ||
1073 (now - this.mExitBackTimeout) > RELEASE_EXIT_CHECK_TIMEOUT);
1075 //Check if there no history and if the user was advised in the last back action
1076 if (this.mExitFlag && (this.mExitFlag != flag || timeout)) {
1077 //Communicate the user that the next time the application will be closed
1078 this.mExitBackTimeout = System.currentTimeMillis();
1079 DialogHelper.showToast(this, R.string.msgs_push_again_to_exit, Toast.LENGTH_SHORT);
1083 //Back action not applied
1084 return !this.mExitFlag;
1091 public boolean onSearchRequested() {
1092 Bundle bundle = new Bundle();
1094 SearchActivity.EXTRA_SEARCH_DIRECTORY,
1095 getCurrentNavigationView().getCurrentDir());
1096 // TODO VoiceSearch icon is not shown. This must be a bug of CM. Verify with a test app.
1097 startSearch(Preferences.getLastSearch(), true, bundle, false);
1102 * Method that returns the history size.
1104 private void clearHistory() {
1105 this.mHistory.clear();
1110 * Method that navigates to the passed history reference.
1112 * @param history The history reference
1113 * @return boolean A problem occurs while navigate
1115 public boolean navigateToHistory(History history) {
1118 History realHistory = this.mHistory.get(history.getPosition());
1120 //Navigate to item. Check what kind of history is
1121 if (realHistory.getItem() instanceof NavigationViewInfoParcelable) {
1123 NavigationViewInfoParcelable info =
1124 (NavigationViewInfoParcelable)realHistory.getItem();
1125 int viewId = info.getId();
1126 NavigationView view = getNavigationView(viewId);
1127 view.onRestoreState(info);
1129 } else if (realHistory.getItem() instanceof SearchInfoParcelable) {
1130 //Search (open search with the search results)
1131 SearchInfoParcelable info = (SearchInfoParcelable)realHistory.getItem();
1132 Intent searchIntent = new Intent(this, SearchActivity.class);
1133 searchIntent.setAction(SearchActivity.ACTION_RESTORE);
1134 searchIntent.putExtra(SearchActivity.EXTRA_SEARCH_RESTORE, (Parcelable)info);
1135 startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH);
1137 //The type is unknown
1138 throw new IllegalArgumentException("Unknown history type"); //$NON-NLS-1$
1141 //Remove the old history
1142 int cc = realHistory.getPosition();
1143 for (int i = this.mHistory.size() - 1; i >= cc; i--) {
1144 this.mHistory.remove(i);
1146 if (this.mHistory.size() == 0) {
1147 getActionBar().setDisplayHomeAsUpEnabled(false);
1148 getActionBar().setHomeButtonEnabled(false);
1154 } catch (Throwable ex) {
1155 if (history != null) {
1157 String.format("Failed to navigate to history %d: %s", //$NON-NLS-1$
1158 Integer.valueOf(history.getPosition()),
1159 history.getItem().getTitle()), ex);
1162 String.format("Failed to navigate to history: null", ex)); //$NON-NLS-1$
1164 this.mHandler.post(new Runnable() {
1167 DialogHelper.showToast(
1168 NavigationActivity.this,
1169 R.string.msgs_history_unknown, Toast.LENGTH_LONG);
1173 //Not change directory
1179 * Method that request a back action over the navigation history.
1181 * @return boolean If a back action was applied
1183 public boolean back() {
1184 // Check that has valid history
1185 while (this.mHistory.size() > 0) {
1186 History h = this.mHistory.get(this.mHistory.size() - 1);
1187 if (h.getItem() instanceof NavigationViewInfoParcelable) {
1188 // Verify that the path exists
1189 String path = ((NavigationViewInfoParcelable)h.getItem()).getCurrentDir();
1192 FileSystemObject info = CommandHelper.getFileInfo(this, path, null);
1196 this.mHistory.remove(this.mHistory.size() - 1);
1197 } catch (Exception e) {
1198 ExceptionUtil.translateException(this, e, true, false);
1199 this.mHistory.remove(this.mHistory.size() - 1);
1206 //Extract a history from the
1207 if (this.mHistory.size() > 0) {
1208 //Navigate to history
1209 return navigateToHistory(this.mHistory.get(this.mHistory.size() - 1));
1217 * Method that opens the actions dialog
1219 * @param item The path or the {@link FileSystemObject}
1220 * @param global If the menu to display is the one with global actions
1222 private void openActionsDialog(Object item, boolean global) {
1223 // Resolve the full path
1224 String path = String.valueOf(item);
1225 if (item instanceof FileSystemObject) {
1226 path = ((FileSystemObject)item).getFullPath();
1229 // Prior to show the dialog, refresh the item reference
1230 FileSystemObject fso = null;
1232 fso = CommandHelper.getFileInfo(this, path, false, null);
1234 throw new NoSuchFileOrDirectory(path);
1237 } catch (Exception e) {
1239 ExceptionUtil.translateException(this, e);
1241 // Remove the object
1242 if (e instanceof FileNotFoundException || e instanceof NoSuchFileOrDirectory) {
1243 // If have a FileSystemObject reference then there is no need to search
1244 // the path (less resources used)
1245 if (item instanceof FileSystemObject) {
1246 getCurrentNavigationView().removeItem((FileSystemObject)item);
1248 getCurrentNavigationView().removeItem((String)item);
1255 ActionsDialog dialog = new ActionsDialog(this, fso, global, false);
1256 dialog.setOnRequestRefreshListener(this);
1257 dialog.setOnSelectionListener(getCurrentNavigationView());
1262 * Method that opens the bookmarks activity.
1265 void openBookmarks() {
1266 Intent bookmarksIntent = new Intent(this, BookmarksActivity.class);
1267 bookmarksIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
1268 startActivityForResult(bookmarksIntent, INTENT_REQUEST_BOOKMARK);
1272 * Method that opens the history activity.
1275 void openHistory() {
1276 Intent historyIntent = new Intent(this, HistoryActivity.class);
1277 historyIntent.putExtra(HistoryActivity.EXTRA_HISTORY_LIST, (Serializable)this.mHistory);
1278 historyIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
1279 startActivityForResult(historyIntent, INTENT_REQUEST_HISTORY);
1283 * Method that opens the search activity.
1287 onSearchRequested();
1291 * Method that remove the {@link FileSystemObject} from the history
1293 private void removeFromHistory(FileSystemObject fso) {
1294 if (this.mHistory != null) {
1295 int cc = this.mHistory.size();
1296 for (int i = cc-1; i >= 0 ; i--) {
1297 History history = this.mHistory.get(i);
1298 if (history.getItem() instanceof NavigationViewInfoParcelable) {
1299 String p0 = fso.getFullPath();
1301 ((NavigationViewInfoParcelable)history.getItem()).getCurrentDir();
1302 if (p0.compareTo(p1) == 0) {
1303 this.mHistory.remove(i);
1311 * Method that ask the user to change to to advanced console
1315 //Show a dialog asking the user
1316 AlertDialog dialog =
1317 DialogHelper.createYesNoDialog(
1318 this, R.string.msgs_change_to_advanced_console_mode,
1319 new DialogInterface.OnClickListener() {
1321 public void onClick(DialogInterface alertDialog, int which) {
1322 if (which == DialogInterface.BUTTON_NEGATIVE) {
1323 // We don't have any console
1324 // Show exception and exists
1325 DialogHelper.showToast(
1326 NavigationActivity.this,
1327 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
1332 // Ok. Now try to change to advanced selection console. Any crash
1333 // here is a fatal error. We won't have any console to operate.
1336 ConsoleBuilder.changeToNonPrivilegedConsole(NavigationActivity.this);
1339 Preferences.savePreference(
1340 FileManagerSettings.SETTINGS_ADVANCE_MODE,
1341 Boolean.TRUE, true);
1342 Preferences.savePreference(
1343 FileManagerSettings.SETTINGS_SUPERUSER_MODE,
1344 Boolean.FALSE, true);
1346 } catch (Exception e) {
1347 //Show exception and exists
1348 Log.e(TAG, getString(R.string.msgs_cant_create_console), e);
1349 DialogHelper.showToast(
1350 NavigationActivity.this,
1351 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
1360 * Method that creates a ChRooted environment, protecting the user to break anything in
1364 void createChRooted() {
1365 // If we are in a ChRooted mode, then do nothing
1366 if (this.mChRooted) return;
1367 this.mChRooted = true;
1369 int cc = this.mNavigationViews.length;
1370 for (int i = 0; i < cc; i++) {
1371 this.mNavigationViews[i].createChRooted();
1374 // Remove the selection
1375 cc = this.mNavigationViews.length;
1376 for (int i = 0; i < cc; i++) {
1377 getCurrentNavigationView().onDeselectAll();
1380 // Remove the history (don't allow to access to previous data)
1385 * Method that exits from a ChRooted
1388 void exitChRooted() {
1389 // If we aren't in a ChRooted mode, then do nothing
1390 if (!this.mChRooted) return;
1391 this.mChRooted = false;
1393 int cc = this.mNavigationViews.length;
1394 for (int i = 0; i < cc; i++) {
1395 this.mNavigationViews[i].exitChRooted();
1400 * Method called when a controlled exit is required
1405 FileManagerApplication.destroyBackgroundConsole();
1406 } catch (Throwable ex) {
1410 ConsoleBuilder.destroyConsole();
1411 } catch (Throwable ex) {