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.AccessMode;
65 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
66 import com.cyanogenmod.filemanager.preferences.NavigationLayoutMode;
67 import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
68 import com.cyanogenmod.filemanager.preferences.Preferences;
69 import com.cyanogenmod.filemanager.ui.dialogs.ActionsDialog;
70 import com.cyanogenmod.filemanager.ui.dialogs.FilesystemInfoDialog;
71 import com.cyanogenmod.filemanager.ui.dialogs.FilesystemInfoDialog.OnMountListener;
72 import com.cyanogenmod.filemanager.ui.widgets.Breadcrumb;
73 import com.cyanogenmod.filemanager.ui.widgets.NavigationCustomTitleView;
74 import com.cyanogenmod.filemanager.ui.widgets.NavigationView;
75 import com.cyanogenmod.filemanager.ui.widgets.NavigationView.OnNavigationRequestMenuListener;
76 import com.cyanogenmod.filemanager.ui.widgets.NavigationView.OnNavigationSelectionChangedListener;
77 import com.cyanogenmod.filemanager.ui.widgets.SelectionView;
78 import com.cyanogenmod.filemanager.util.AndroidHelper;
79 import com.cyanogenmod.filemanager.util.CommandHelper;
80 import com.cyanogenmod.filemanager.util.DialogHelper;
81 import com.cyanogenmod.filemanager.util.ExceptionUtil;
82 import com.cyanogenmod.filemanager.util.FileHelper;
83 import com.cyanogenmod.filemanager.util.StorageHelper;
85 import java.io.FileNotFoundException;
86 import java.io.Serializable;
87 import java.util.ArrayList;
88 import java.util.Arrays;
89 import java.util.List;
92 * The main navigation activity. This activity is the center of the application.
93 * From this the user can navigate, search, make actions.<br/>
94 * This activity is singleTop, so when it is displayed no other activities exists in
96 * This cause an issue with the saved instance of this class, because if another activity
97 * is displayed, and the process is killed, NavigationActivity is started and the saved
98 * instance gets corrupted.<br/>
99 * For this reason the methods {link {@link Activity#onSaveInstanceState(Bundle)} and
100 * {@link Activity#onRestoreInstanceState(Bundle)} are not implemented, and every time
101 * the app is killed, is restarted from his initial state.
103 public class NavigationActivity extends Activity
104 implements OnHistoryListener, OnRequestRefreshListener,
105 OnNavigationRequestMenuListener, OnNavigationSelectionChangedListener {
107 private static final String TAG = "NavigationActivity"; //$NON-NLS-1$
109 private static boolean DEBUG = false;
112 * Intent code for request a bookmark selection.
114 public static final int INTENT_REQUEST_BOOKMARK = 10001;
117 * Intent code for request a history selection.
119 public static final int INTENT_REQUEST_HISTORY = 20001;
122 * Intent code for request a search.
124 public static final int INTENT_REQUEST_SEARCH = 30001;
128 * Constant for extra information about selected bookmark.
130 public static final String EXTRA_BOOKMARK_SELECTION =
131 "extra_bookmark_selection"; //$NON-NLS-1$
134 * Constant for extra information about selected history entry.
136 public static final String EXTRA_HISTORY_ENTRY_SELECTION =
137 "extra_history_entry_selection"; //$NON-NLS-1$
140 * Constant for extra information about clear selection action.
142 public static final String EXTRA_HISTORY_CLEAR =
143 "extra_history_clear_history"; //$NON-NLS-1$
146 * Constant for extra information about selected search entry.
148 public static final String EXTRA_SEARCH_ENTRY_SELECTION =
149 "extra_search_entry_selection"; //$NON-NLS-1$
152 * Constant for extra information about last search data.
154 public static final String EXTRA_SEARCH_LAST_SEARCH_DATA =
155 "extra_search_last_search_data"; //$NON-NLS-1$
158 * Constant for extra information for request a navigation to the passed path.
160 public static final String EXTRA_NAVIGATE_TO =
161 "extra_navigate_to"; //$NON-NLS-1$
163 // The timeout needed to reset the exit status for back button
164 // After this time user need to tap 2 times the back button to
165 // exit, and the toast is shown again after the first tap.
166 private static final int RELEASE_EXIT_CHECK_TIMEOUT = 3500;
168 private final BroadcastReceiver mOnSettingChangeReceiver = new BroadcastReceiver() {
170 public void onReceive(Context context, Intent intent) {
171 if (intent != null) {
172 if (intent.getAction().compareTo(FileManagerSettings.INTENT_SETTING_CHANGED) == 0) {
173 // The settings has changed
174 String key = intent.getStringExtra(FileManagerSettings.EXTRA_SETTING_CHANGED_KEY);
176 // Disk usage warning level
177 if (key.compareTo(FileManagerSettings.
178 SETTINGS_DISK_USAGE_WARNING_LEVEL.getId()) == 0) {
180 // Set the free disk space warning level of the breadcrumb widget
181 Breadcrumb breadcrumb = getCurrentNavigationView().getBreadcrumb();
182 String fds = Preferences.getSharedPreferences().getString(
183 FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
184 (String)FileManagerSettings.
185 SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
186 breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
187 breadcrumb.updateMountPointInfo();
191 // Case sensitive sort
192 if (key.compareTo(FileManagerSettings.
193 SETTINGS_CASE_SENSITIVE_SORT.getId()) == 0) {
194 getCurrentNavigationView().refresh();
199 if (key.compareTo(FileManagerSettings.
200 SETTINGS_ACCESS_MODE.getId()) == 0) {
201 // Is it necessary to create or exit of the ChRooted?
203 FileManagerApplication.
204 getAccessMode().compareTo(AccessMode.SAFE) == 0;
205 if (chRooted != NavigationActivity.this.mChRooted) {
215 } else if (intent.getAction().compareTo(
216 FileManagerSettings.INTENT_FILE_CHANGED) == 0) {
217 // Retrieve the file that was changed
219 intent.getStringExtra(FileManagerSettings.EXTRA_FILE_CHANGED_KEY);
221 FileSystemObject fso = CommandHelper.getFileInfo(context, file, null);
223 getCurrentNavigationView().refresh(fso);
225 } catch (Exception e) {
226 ExceptionUtil.translateException(context, e, true, false);
236 NavigationView[] mNavigationViews;
237 private List<History> mHistory;
239 private int mCurrentNavigationView;
241 private ViewGroup mActionBar;
242 private SelectionView mSelectionBar;
244 private boolean mExitFlag = false;
245 private long mExitBackTimeout = -1;
247 private View mOptionsAnchorView;
263 protected void onCreate(Bundle state) {
266 Log.d(TAG, "NavigationActivity.onCreate"); //$NON-NLS-1$
269 // Register the broadcast receiver
270 IntentFilter filter = new IntentFilter();
271 filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED);
272 filter.addAction(FileManagerSettings.INTENT_FILE_CHANGED);
273 registerReceiver(this.mOnSettingChangeReceiver, filter);
276 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
278 //Set the main layout of the activity
279 setContentView(R.layout.navigation);
281 // Show welcome message
284 //Initialize activity
288 initNavigationViews();
290 //Initialize action bars
291 initTitleActionBar();
292 initStatusActionBar();
295 this.mHandler = new Handler();
296 this.mHandler.post(new Runnable() {
299 //Initialize navigation
300 int cc = NavigationActivity.this.mNavigationViews.length;
301 for (int i = 0; i < cc; i++) {
302 initNavigation(i, false);
305 //Check the intent action
306 checkIntent(getIntent());
311 super.onCreate(state);
318 protected void onNewIntent(Intent intent) {
319 //Initialize navigation
320 initNavigation(this.mCurrentNavigationView, true);
322 //Check the intent action
330 protected void onDestroy() {
332 Log.d(TAG, "NavigationActivity.onDestroy"); //$NON-NLS-1$
335 // Unregister the receiver
337 unregisterReceiver(this.mOnSettingChangeReceiver);
338 } catch (Throwable ex) {
342 //All destroy. Continue
347 * Method that returns the current navigation view.
349 * @return NavigationView The current navigation view
351 public NavigationView getCurrentNavigationView() {
352 return getNavigationView(this.mCurrentNavigationView);
356 * Method that returns the current navigation view.
358 * @param viewId The view to return
359 * @return NavigationView The current navigation view
361 public NavigationView getNavigationView(int viewId) {
362 return this.mNavigationViews[viewId];
366 * Method that initializes the activity.
368 private void init() {
369 this.mHistory = new ArrayList<History>();
370 this.mChRooted = FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
374 * Method that displays a welcome message the first time the user
375 * access the application
377 private void showWelcomeMsg() {
378 boolean firstUse = Preferences.getSharedPreferences().getBoolean(
379 FileManagerSettings.SETTINGS_FIRST_USE.getId(),
380 ((Boolean)FileManagerSettings.SETTINGS_FIRST_USE.getDefaultValue()).booleanValue());
382 //Display the welcome message?
384 AlertDialog dialog = DialogHelper.createAlertDialog(
385 this, R.drawable.ic_launcher, R.string.welcome_title, R.string.welcome_msg, false);
388 // Don't display again this dialog
390 Preferences.savePreference(
391 FileManagerSettings.SETTINGS_FIRST_USE, Boolean.FALSE, true);
392 } catch (Exception e) {/**NON BLOCK**/}
397 * Method that initializes the titlebar of the activity.
399 private void initTitleActionBar() {
400 //Inflate the view and associate breadcrumb
401 NavigationCustomTitleView title =
402 (NavigationCustomTitleView)getLayoutInflater().inflate(
403 R.layout.navigation_view_customtitle, null, false);
404 title.setOnHistoryListener(this);
405 Breadcrumb breadcrumb = (Breadcrumb)title.findViewById(R.id.breadcrumb_view);
406 int cc = this.mNavigationViews.length;
407 for (int i = 0; i < cc; i++) {
408 this.mNavigationViews[i].setBreadcrumb(breadcrumb);
409 this.mNavigationViews[i].setOnHistoryListener(this);
410 this.mNavigationViews[i].setOnNavigationSelectionChangedListener(this);
411 this.mNavigationViews[i].setOnNavigationOnRequestMenuListener(this);
412 this.mNavigationViews[i].setCustomTitle(title);
415 // Set the free disk space warning level of the breadcrumb widget
416 String fds = Preferences.getSharedPreferences().getString(
417 FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
418 (String)FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
419 breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
421 //Configure the action bar options
422 getActionBar().setBackgroundDrawable(
423 getResources().getDrawable(R.drawable.bg_holo_titlebar));
424 getActionBar().setDisplayOptions(
425 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
426 getActionBar().setCustomView(title);
430 * Method that initializes the statusbar of the activity.
432 private void initStatusActionBar() {
433 //Performs a width calculation of buttons. Buttons exceeds the width
434 //of the action bar should be hidden
435 //This application not use android ActionBar because the application
436 //make uses of the title and bottom areas, and wants to force to show
437 //the overflow button (without care of physical buttons)
438 this.mActionBar = (ViewGroup)findViewById(R.id.navigation_actionbar);
439 this.mActionBar.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
441 public void onLayoutChange(
442 View v, int left, int top, int right, int bottom, int oldLeft,
443 int oldTop, int oldRight, int oldBottom) {
444 //Get the width of the action bar
445 int w = v.getMeasuredWidth();
447 //Wake through children calculation his dimensions
448 int bw = (int)getResources().getDimension(R.dimen.default_buttom_width);
450 final ViewGroup abView = ((ViewGroup)v);
451 int cc = abView.getChildCount();
452 for (int i = 0; i < cc; i++) {
453 View child = abView.getChildAt(i);
454 child.setVisibility(cw + bw > w ? View.INVISIBLE : View.VISIBLE);
460 // Have overflow menu?
461 View overflow = findViewById(R.id.ab_overflow);
462 boolean showOptionsMenu = AndroidHelper.showOptionsMenu(getApplicationContext());
463 overflow.setVisibility(showOptionsMenu ? View.VISIBLE : View.GONE);
464 this.mOptionsAnchorView = showOptionsMenu ? overflow : this.mActionBar;
468 * Method that initializes the selectionbar of the activity.
470 private void initSelectionBar() {
471 this.mSelectionBar = (SelectionView)findViewById(R.id.navigation_selectionbar);
475 * Method that initializes the navigation views of the activity
477 private void initNavigationViews() {
478 //Get the navigation views (wishlist: multiple view; for now only one view)
479 this.mNavigationViews = new NavigationView[1];
480 this.mCurrentNavigationView = 0;
482 this.mNavigationViews[0] = (NavigationView)findViewById(R.id.navigation_view);
483 this.mNavigationViews[0].setId(0);
487 * Method that initializes the navigation.
489 * @param viewId The navigation view identifier where apply the navigation
490 * @param restore Initialize from a restore info
493 void initNavigation(final int viewId, final boolean restore) {
494 final NavigationView navigationView = getNavigationView(viewId);
495 this.mHandler.post(new Runnable() {
498 //Create the default console (from the preferences)
500 Console console = ConsoleBuilder.getConsole(NavigationActivity.this);
501 if (console == null) {
502 throw new ConsoleAllocException("console == null"); //$NON-NLS-1$
504 } catch (Throwable ex) {
505 if (!NavigationActivity.this.mChRooted) {
506 //Show exception and exists
507 Log.e(TAG, getString(R.string.msgs_cant_create_console), ex);
508 // We don't have any console
509 // Show exception and exists
510 DialogHelper.showToast(
511 NavigationActivity.this,
512 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
517 // We are in a trouble (something is not allowing creating the console)
518 // Ask the user to return to prompt or root access mode mode with a
519 // non-privileged console, prior to make crash the application
524 //Is necessary navigate?
526 //Load the preference initial directory
528 Preferences.getSharedPreferences().getString(
529 FileManagerSettings.SETTINGS_INITIAL_DIR.getId(),
530 (String)FileManagerSettings.
531 SETTINGS_INITIAL_DIR.getDefaultValue());
532 if (NavigationActivity.this.mChRooted) {
533 // Initial directory is the first external sdcard (sdcard, emmc, usb, ...)
534 StorageVolume[] volumes =
535 StorageHelper.getStorageVolumes(NavigationActivity.this);
536 if (volumes != null && volumes.length > 0) {
537 initialDir = volumes[0].getPath();
541 //Ensure initial is an absolute directory
544 CommandHelper.getAbsolutePath(
545 NavigationActivity.this, initialDir, null);
546 } catch (Throwable e) {
547 Log.e(TAG, "Resolve of initital directory fails", e); //$NON-NLS-1$
550 R.string.msgs_settings_invalid_initial_directory,
552 DialogHelper.showToast(NavigationActivity.this, msg, Toast.LENGTH_SHORT);
553 initialDir = FileHelper.ROOT_DIRECTORY;
556 // Change the current directory to the preference initial directory or the
558 String navigateTo = getIntent().getStringExtra(EXTRA_NAVIGATE_TO);
559 if (navigateTo != null && navigateTo.length() > 0) {
560 navigationView.changeCurrentDir(navigateTo);
562 navigationView.changeCurrentDir(initialDir);
570 * Method that verifies the intent passed to the activity, and checks
571 * if a request is made like Search.
573 * @param intent The intent to check
576 void checkIntent(Intent intent) {
578 if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
579 Intent searchIntent = new Intent(this, SearchActivity.class);
580 searchIntent.setAction(Intent.ACTION_SEARCH);
581 //- SearchActivity.EXTRA_SEARCH_DIRECTORY
582 searchIntent.putExtra(
583 SearchActivity.EXTRA_SEARCH_DIRECTORY,
584 getCurrentNavigationView().getCurrentDir());
585 //- SearchManager.APP_DATA
586 if (intent.getBundleExtra(SearchManager.APP_DATA) != null) {
587 Bundle bundle = new Bundle();
588 bundle.putAll(intent.getBundleExtra(SearchManager.APP_DATA));
589 searchIntent.putExtra(SearchManager.APP_DATA, bundle);
591 //-- SearchManager.QUERY
592 String query = intent.getStringExtra(SearchManager.QUERY);
594 searchIntent.putExtra(SearchManager.QUERY, query);
596 //- android.speech.RecognizerIntent.EXTRA_RESULTS
597 ArrayList<String> extraResults =
598 intent.getStringArrayListExtra(android.speech.RecognizerIntent.EXTRA_RESULTS);
599 if (extraResults != null) {
600 searchIntent.putStringArrayListExtra(
601 android.speech.RecognizerIntent.EXTRA_RESULTS, extraResults);
603 startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH);
607 // Navigate to the requested path
608 String navigateTo = intent.getStringExtra(EXTRA_NAVIGATE_TO);
609 if (navigateTo != null && navigateTo.length() >= 0) {
610 getCurrentNavigationView().changeCurrentDir(navigateTo);
619 public boolean onKeyUp(int keyCode, KeyEvent event) {
620 if (keyCode == KeyEvent.KEYCODE_MENU) {
621 showOverflowPopUp(this.mOptionsAnchorView);
624 if (keyCode == KeyEvent.KEYCODE_BACK) {
625 if (checkBackAction()) {
629 // An exit event has occurred, force the destroy the consoles
632 return super.onKeyUp(keyCode, event);
639 public boolean onOptionsItemSelected(MenuItem item) {
640 switch (item.getItemId()) {
641 case android.R.id.home:
642 if ((getActionBar().getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP)
643 == ActionBar.DISPLAY_HOME_AS_UP) {
648 return super.onOptionsItemSelected(item);
653 * Method invoked when an action item is clicked.
655 * @param view The button pushed
657 public void onActionBarItemClick(View view) {
658 switch (view.getId()) {
659 //######################
660 //Navigation Custom Title
661 //######################
662 case R.id.ab_configuration:
663 //Show navigation view configuration toolbar
664 getCurrentNavigationView().getCustomTitle().showConfigurationView();
665 getActionBar().setDisplayHomeAsUpEnabled(true);
666 getActionBar().setHomeButtonEnabled(true);
669 //Hide navigation view configuration toolbar
670 getCurrentNavigationView().getCustomTitle().hideConfigurationView();
673 //######################
675 //######################
676 case R.id.ab_filesystem_info:
677 //Show information of the filesystem
678 MountPoint mp = getCurrentNavigationView().getBreadcrumb().getMountPointInfo();
679 DiskUsage du = getCurrentNavigationView().getBreadcrumb().getDiskUsageInfo();
680 showMountPointInfo(mp, du);
683 //######################
684 //Navigation view options
685 //######################
686 case R.id.ab_sort_mode:
687 showSettingsPopUp(view,
689 new FileManagerSettings[]{
690 FileManagerSettings.SETTINGS_SORT_MODE}));
692 case R.id.ab_layout_mode:
693 showSettingsPopUp(view,
695 new FileManagerSettings[]{
696 FileManagerSettings.SETTINGS_LAYOUT_MODE}));
698 case R.id.ab_view_options:
699 // If we are in ChRooted mode, then don't show non-secure items
700 if (this.mChRooted) {
701 showSettingsPopUp(view,
702 Arrays.asList(new FileManagerSettings[]{
703 FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST}));
705 showSettingsPopUp(view,
706 Arrays.asList(new FileManagerSettings[]{
707 FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST,
708 FileManagerSettings.SETTINGS_SHOW_HIDDEN,
709 FileManagerSettings.SETTINGS_SHOW_SYSTEM,
710 FileManagerSettings.SETTINGS_SHOW_SYMLINKS}));
715 //######################
717 //######################
718 case R.id.ab_selection_done:
719 //Show information of the filesystem
720 getCurrentNavigationView().onDeselectAll();
723 //######################
725 //######################
726 case R.id.ab_actions:
727 openActionsDialog(getCurrentNavigationView().getCurrentDir(), true);
730 case R.id.ab_bookmarks:
734 case R.id.ab_history:
742 case R.id.ab_overflow:
743 showOverflowPopUp(view);
755 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
757 switch (requestCode) {
758 case INTENT_REQUEST_BOOKMARK:
759 if (resultCode == RESULT_OK) {
760 FileSystemObject fso =
761 (FileSystemObject)data.
762 getSerializableExtra(EXTRA_BOOKMARK_SELECTION);
765 getCurrentNavigationView().open(fso);
770 case INTENT_REQUEST_HISTORY:
771 if (resultCode == RESULT_OK) {
772 //Change current directory
774 (History)data.getSerializableExtra(EXTRA_HISTORY_ENTRY_SELECTION);
775 navigateToHistory(history);
776 } else if (resultCode == RESULT_CANCELED) {
777 boolean clear = data.getBooleanExtra(EXTRA_HISTORY_CLEAR, false);
784 case INTENT_REQUEST_SEARCH:
785 if (resultCode == RESULT_OK) {
787 FileSystemObject fso =
788 (FileSystemObject)data.
789 getSerializableExtra(EXTRA_SEARCH_ENTRY_SELECTION);
790 SearchInfoParcelable searchInfo =
791 data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
793 //Goto to new directory
794 getCurrentNavigationView().open(fso, searchInfo);
796 } else if (resultCode == RESULT_CANCELED) {
797 SearchInfoParcelable searchInfo =
798 data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
799 if (searchInfo != null && searchInfo.isSuccessNavigation()) {
800 //Navigate to previous history
803 // I don't know is the search view was changed, so do a refresh
804 // of the navigation view
805 getCurrentNavigationView().refresh();
820 public void onNewHistory(HistoryNavigable navigable) {
821 //Recollect information about current status
822 History history = new History(this.mHistory.size(), navigable);
823 this.mHistory.add(history);
824 getActionBar().setDisplayHomeAsUpEnabled(true);
825 getActionBar().setHomeButtonEnabled(true);
832 public void onCheckHistory() {
833 //Need to show HomeUp Button
834 boolean enabled = this.mHistory != null && this.mHistory.size() > 0;
835 getActionBar().setDisplayHomeAsUpEnabled(enabled);
836 getActionBar().setHomeButtonEnabled(enabled);
843 public void onRequestRefresh(Object o) {
844 if (o instanceof FileSystemObject) {
845 // Refresh only the item
846 this.getCurrentNavigationView().refresh((FileSystemObject)o);
847 } else if (o == null) {
849 getCurrentNavigationView().refresh();
851 this.getCurrentNavigationView().onDeselectAll();
858 public void onRequestRemove(Object o) {
859 if (o instanceof FileSystemObject) {
861 this.getCurrentNavigationView().removeItem((FileSystemObject)o);
863 //Remove from history
864 removeFromHistory((FileSystemObject)o);
866 this.getCurrentNavigationView().onDeselectAll();
873 public void onNavigateTo(Object o) {
881 public void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems) {
882 this.mSelectionBar.setSelection(selectedItems);
889 public void onRequestMenu(NavigationView navView, FileSystemObject item) {
890 // Show the actions dialog
891 openActionsDialog(item, false);
895 * Method that shows a popup with a menu associated a {@link FileManagerSettings}.
897 * @param anchor The action button that was pressed
898 * @param settings The array of settings associated with the action button
900 private void showSettingsPopUp(View anchor, List<FileManagerSettings> settings) {
902 final MenuSettingsAdapter adapter = new MenuSettingsAdapter(this, settings);
904 //Create a show the popup menu
905 final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor);
906 popup.setOnItemClickListener(new OnItemClickListener() {
908 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
909 FileManagerSettings setting =
910 ((MenuSettingsAdapter)parent.getAdapter()).getSetting(position);
911 final int value = ((MenuSettingsAdapter)parent.getAdapter()).getId(position);
914 if (setting.compareTo(FileManagerSettings.SETTINGS_LAYOUT_MODE) == 0) {
915 //Need to change the layout
916 getCurrentNavigationView().changeViewMode(
917 NavigationLayoutMode.fromId(value));
920 if (setting.getDefaultValue() instanceof Enum<?>) {
922 Preferences.savePreference(setting, new ObjectIdentifier() {
931 Preferences.getSharedPreferences().
934 ((Boolean)setting.getDefaultValue()).booleanValue());
935 Preferences.savePreference(setting, Boolean.valueOf(!newval), false);
937 getCurrentNavigationView().refresh();
939 } catch (Exception e) {
940 Log.e(TAG, "Error applying navigation option", e); //$NON-NLS-1$
941 NavigationActivity.this.mHandler.post(new Runnable() {
944 DialogHelper.showToast(
945 NavigationActivity.this,
946 R.string.msgs_settings_save_failure, Toast.LENGTH_SHORT);
952 getCurrentNavigationView().getCustomTitle().restoreView();
957 popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
959 public void onDismiss() {
967 * Method that shows a popup with the activity main menu.
969 * @param anchor The action button that was pressed
971 private void showOverflowPopUp(View anchor) {
972 SimpleMenuListAdapter adapter =
973 new HighlightedSimpleMenuListAdapter(this, R.menu.navigation);
974 Menu menu = adapter.getMenu();
975 int cc = this.mActionBar.getChildCount();
976 for (int i = 0, j = this.mActionBar.getChildCount() - 1; i < cc; i++, j--) {
977 View child = this.mActionBar.getChildAt(i);
978 boolean visible = child.getVisibility() == View.VISIBLE;
980 menu.removeItem(menu.getItem(j).getItemId());
984 final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor);
985 popup.setOnItemClickListener(new OnItemClickListener() {
987 public void onItemClick(
988 final AdapterView<?> parent, final View v, final int position, final long id) {
990 final int itemId = (int)id;
991 NavigationActivity.this.mHandler.post(new Runnable() {
996 case R.id.mnu_settings:
998 Intent settings = new Intent(
999 NavigationActivity.this, SettingsPreferences.class);
1000 startActivity(settings);
1003 case R.id.mnu_history:
1009 case R.id.mnu_bookmarks:
1015 case R.id.mnu_search:
1031 * Method that show the information of a filesystem mount point.
1033 * @param mp The mount point info
1034 * @param du The disk usage of the mount point
1036 private void showMountPointInfo(MountPoint mp, DiskUsage du) {
1037 //Has mount point info?
1039 //There is no information
1041 DialogHelper.createWarningDialog(
1043 R.string.filesystem_info_warning_title,
1044 R.string.filesystem_info_warning_msg);
1049 //Show a the filesystem info dialog
1050 FilesystemInfoDialog dialog = new FilesystemInfoDialog(this, mp, du);
1051 dialog.setOnMountListener(new OnMountListener() {
1053 public void onRemount(MountPoint mountPoint) {
1054 //Update the statistics of breadcrumb, only if mount point is the same
1055 Breadcrumb breadcrumb = getCurrentNavigationView().getBreadcrumb();
1056 if (breadcrumb.getMountPointInfo().compareTo(mountPoint) == 0) {
1057 breadcrumb.updateMountPointInfo();
1065 * Method that checks the action that must be realized when the
1066 * back button is pushed.
1068 * @return boolean Indicates if the action must be intercepted
1070 private boolean checkBackAction() {
1071 //Check if the configuration view is showing. In this case back
1072 //action must be "close configuration"
1073 if (getCurrentNavigationView().getCustomTitle().isConfigurationViewShowing()) {
1074 getCurrentNavigationView().getCustomTitle().restoreView();
1078 //Do back operation over the navigation history
1079 boolean flag = this.mExitFlag;
1081 this.mExitFlag = !back();
1083 // Retrieve if the exit status timeout has expired
1084 long now = System.currentTimeMillis();
1085 boolean timeout = (this.mExitBackTimeout == -1 ||
1086 (now - this.mExitBackTimeout) > RELEASE_EXIT_CHECK_TIMEOUT);
1088 //Check if there no history and if the user was advised in the last back action
1089 if (this.mExitFlag && (this.mExitFlag != flag || timeout)) {
1090 //Communicate the user that the next time the application will be closed
1091 this.mExitBackTimeout = System.currentTimeMillis();
1092 DialogHelper.showToast(this, R.string.msgs_push_again_to_exit, Toast.LENGTH_SHORT);
1096 //Back action not applied
1097 return !this.mExitFlag;
1104 public boolean onSearchRequested() {
1105 Bundle bundle = new Bundle();
1107 SearchActivity.EXTRA_SEARCH_DIRECTORY,
1108 getCurrentNavigationView().getCurrentDir());
1109 // TODO VoiceSearch icon is not shown. This must be a bug of CM. Verify with a test app.
1110 startSearch(Preferences.getLastSearch(), true, bundle, false);
1115 * Method that returns the history size.
1117 private void clearHistory() {
1118 this.mHistory.clear();
1123 * Method that navigates to the passed history reference.
1125 * @param history The history reference
1126 * @return boolean A problem occurs while navigate
1128 public boolean navigateToHistory(History history) {
1131 History realHistory = this.mHistory.get(history.getPosition());
1133 //Navigate to item. Check what kind of history is
1134 if (realHistory.getItem() instanceof NavigationViewInfoParcelable) {
1136 NavigationViewInfoParcelable info =
1137 (NavigationViewInfoParcelable)realHistory.getItem();
1138 int viewId = info.getId();
1139 NavigationView view = getNavigationView(viewId);
1140 view.onRestoreState(info);
1142 } else if (realHistory.getItem() instanceof SearchInfoParcelable) {
1143 //Search (open search with the search results)
1144 SearchInfoParcelable info = (SearchInfoParcelable)realHistory.getItem();
1145 Intent searchIntent = new Intent(this, SearchActivity.class);
1146 searchIntent.setAction(SearchActivity.ACTION_RESTORE);
1147 searchIntent.putExtra(SearchActivity.EXTRA_SEARCH_RESTORE, (Parcelable)info);
1148 startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH);
1150 //The type is unknown
1151 throw new IllegalArgumentException("Unknown history type"); //$NON-NLS-1$
1154 //Remove the old history
1155 int cc = realHistory.getPosition();
1156 for (int i = this.mHistory.size() - 1; i >= cc; i--) {
1157 this.mHistory.remove(i);
1159 if (this.mHistory.size() == 0) {
1160 getActionBar().setDisplayHomeAsUpEnabled(false);
1161 getActionBar().setHomeButtonEnabled(false);
1167 } catch (Throwable ex) {
1168 if (history != null) {
1170 String.format("Failed to navigate to history %d: %s", //$NON-NLS-1$
1171 Integer.valueOf(history.getPosition()),
1172 history.getItem().getTitle()), ex);
1175 String.format("Failed to navigate to history: null", ex)); //$NON-NLS-1$
1177 this.mHandler.post(new Runnable() {
1180 DialogHelper.showToast(
1181 NavigationActivity.this,
1182 R.string.msgs_history_unknown, Toast.LENGTH_LONG);
1186 //Not change directory
1192 * Method that request a back action over the navigation history.
1194 * @return boolean If a back action was applied
1196 public boolean back() {
1197 // Check that has valid history
1198 while (this.mHistory.size() > 0) {
1199 History h = this.mHistory.get(this.mHistory.size() - 1);
1200 if (h.getItem() instanceof NavigationViewInfoParcelable) {
1201 // Verify that the path exists
1202 String path = ((NavigationViewInfoParcelable)h.getItem()).getCurrentDir();
1205 FileSystemObject info = CommandHelper.getFileInfo(this, path, null);
1209 this.mHistory.remove(this.mHistory.size() - 1);
1210 } catch (Exception e) {
1211 ExceptionUtil.translateException(this, e, true, false);
1212 this.mHistory.remove(this.mHistory.size() - 1);
1219 //Extract a history from the
1220 if (this.mHistory.size() > 0) {
1221 //Navigate to history
1222 return navigateToHistory(this.mHistory.get(this.mHistory.size() - 1));
1230 * Method that opens the actions dialog
1232 * @param item The path or the {@link FileSystemObject}
1233 * @param global If the menu to display is the one with global actions
1235 private void openActionsDialog(Object item, boolean global) {
1236 // Resolve the full path
1237 String path = String.valueOf(item);
1238 if (item instanceof FileSystemObject) {
1239 path = ((FileSystemObject)item).getFullPath();
1242 // Prior to show the dialog, refresh the item reference
1243 FileSystemObject fso = null;
1245 fso = CommandHelper.getFileInfo(this, path, false, null);
1247 throw new NoSuchFileOrDirectory(path);
1250 } catch (Exception e) {
1252 ExceptionUtil.translateException(this, e);
1254 // Remove the object
1255 if (e instanceof FileNotFoundException || e instanceof NoSuchFileOrDirectory) {
1256 // If have a FileSystemObject reference then there is no need to search
1257 // the path (less resources used)
1258 if (item instanceof FileSystemObject) {
1259 getCurrentNavigationView().removeItem((FileSystemObject)item);
1261 getCurrentNavigationView().removeItem((String)item);
1268 ActionsDialog dialog = new ActionsDialog(this, fso, global, false);
1269 dialog.setOnRequestRefreshListener(this);
1270 dialog.setOnSelectionListener(getCurrentNavigationView());
1275 * Method that opens the bookmarks activity.
1278 void openBookmarks() {
1279 Intent bookmarksIntent = new Intent(this, BookmarksActivity.class);
1280 bookmarksIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
1281 startActivityForResult(bookmarksIntent, INTENT_REQUEST_BOOKMARK);
1285 * Method that opens the history activity.
1288 void openHistory() {
1289 Intent historyIntent = new Intent(this, HistoryActivity.class);
1290 historyIntent.putExtra(HistoryActivity.EXTRA_HISTORY_LIST, (Serializable)this.mHistory);
1291 historyIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
1292 startActivityForResult(historyIntent, INTENT_REQUEST_HISTORY);
1296 * Method that opens the search activity.
1300 onSearchRequested();
1304 * Method that remove the {@link FileSystemObject} from the history
1306 private void removeFromHistory(FileSystemObject fso) {
1307 if (this.mHistory != null) {
1308 int cc = this.mHistory.size();
1309 for (int i = cc-1; i >= 0 ; i--) {
1310 History history = this.mHistory.get(i);
1311 if (history.getItem() instanceof NavigationViewInfoParcelable) {
1312 String p0 = fso.getFullPath();
1314 ((NavigationViewInfoParcelable)history.getItem()).getCurrentDir();
1315 if (p0.compareTo(p1) == 0) {
1316 this.mHistory.remove(i);
1324 * Method that ask the user to change the access mode prior to crash.
1328 //Show a dialog asking the user
1329 AlertDialog dialog =
1330 DialogHelper.createYesNoDialog(
1332 R.string.msgs_change_to_prompt_access_mode_title,
1333 R.string.msgs_change_to_prompt_access_mode_msg,
1334 new DialogInterface.OnClickListener() {
1336 public void onClick(DialogInterface alertDialog, int which) {
1337 if (which == DialogInterface.BUTTON_NEGATIVE) {
1338 // We don't have any console
1339 // Show exception and exists
1340 DialogHelper.showToast(
1341 NavigationActivity.this,
1342 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
1347 // Ok. Now try to change to prompt mode. Any crash
1348 // here is a fatal error. We won't have any console to operate.
1351 ConsoleBuilder.changeToNonPrivilegedConsole(NavigationActivity.this);
1354 Preferences.savePreference(
1355 FileManagerSettings.SETTINGS_ACCESS_MODE,
1356 AccessMode.PROMPT, true);
1358 } catch (Exception e) {
1359 // Displays an exception and exit
1360 Log.e(TAG, getString(R.string.msgs_cant_create_console), e);
1361 DialogHelper.showToast(
1362 NavigationActivity.this,
1363 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
1372 * Method that creates a ChRooted environment, protecting the user to break anything in
1376 void createChRooted() {
1377 // If we are in a ChRooted mode, then do nothing
1378 if (this.mChRooted) return;
1379 this.mChRooted = true;
1381 int cc = this.mNavigationViews.length;
1382 for (int i = 0; i < cc; i++) {
1383 this.mNavigationViews[i].createChRooted();
1386 // Remove the selection
1387 cc = this.mNavigationViews.length;
1388 for (int i = 0; i < cc; i++) {
1389 getCurrentNavigationView().onDeselectAll();
1392 // Remove the history (don't allow to access to previous data)
1397 * Method that exits from a ChRooted
1400 void exitChRooted() {
1401 // If we aren't in a ChRooted mode, then do nothing
1402 if (!this.mChRooted) return;
1403 this.mChRooted = false;
1405 int cc = this.mNavigationViews.length;
1406 for (int i = 0; i < cc; i++) {
1407 this.mNavigationViews[i].exitChRooted();
1412 * Method called when a controlled exit is required
1417 FileManagerApplication.destroyBackgroundConsole();
1418 } catch (Throwable ex) {
1422 ConsoleBuilder.destroyConsole();
1423 } catch (Throwable ex) {