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.ChooseConsoleDialog;
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_ADVANCE_MODE.getId()) == 0) {
201 // Is it necessary to create or exit of the ChRooted?
202 boolean chRooted = !FileManagerApplication.isAdvancedMode();
203 if (chRooted != NavigationActivity.this.mChRooted) {
213 } else if (intent.getAction().compareTo(
214 FileManagerSettings.INTENT_FILE_CHANGED) == 0) {
215 // Retrieve the file that was changed
217 intent.getStringExtra(FileManagerSettings.EXTRA_FILE_CHANGED_KEY);
219 FileSystemObject fso = CommandHelper.getFileInfo(context, file, null);
221 getCurrentNavigationView().refresh(fso);
223 } catch (Exception e) {
224 ExceptionUtil.translateException(context, e, true, false);
234 NavigationView[] mNavigationViews;
235 private List<History> mHistory;
237 private int mCurrentNavigationView;
239 private ViewGroup mActionBar;
240 private SelectionView mSelectionBar;
242 private boolean mExitFlag = false;
243 private long mExitBackTimeout = -1;
245 private View mOptionsAnchorView;
261 protected void onCreate(Bundle state) {
264 Log.d(TAG, "NavigationActivity.onCreate"); //$NON-NLS-1$
267 // Register the broadcast receiver
268 IntentFilter filter = new IntentFilter();
269 filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED);
270 filter.addAction(FileManagerSettings.INTENT_FILE_CHANGED);
271 registerReceiver(this.mOnSettingChangeReceiver, filter);
274 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
276 //Set the main layout of the activity
277 setContentView(R.layout.navigation);
279 // Show welcome message
282 //Initialize activity
286 initNavigationViews();
288 //Initialize action bars
289 initTitleActionBar();
290 initStatusActionBar();
293 this.mHandler = new Handler();
294 this.mHandler.post(new Runnable() {
297 //Initialize navigation
298 int cc = NavigationActivity.this.mNavigationViews.length;
299 for (int i = 0; i < cc; i++) {
300 initNavigation(i, false);
303 //Check the intent action
304 checkIntent(getIntent());
309 super.onCreate(state);
316 protected void onNewIntent(Intent intent) {
317 //Initialize navigation
318 initNavigation(this.mCurrentNavigationView, true);
320 //Check the intent action
328 protected void onDestroy() {
330 Log.d(TAG, "NavigationActivity.onDestroy"); //$NON-NLS-1$
333 // Unregister the receiver
335 unregisterReceiver(this.mOnSettingChangeReceiver);
336 } catch (Throwable ex) {
340 //All destroy. Continue
345 * Method that returns the current navigation view.
347 * @return NavigationView The current navigation view
349 public NavigationView getCurrentNavigationView() {
350 return getNavigationView(this.mCurrentNavigationView);
354 * Method that returns the current navigation view.
356 * @param viewId The view to return
357 * @return NavigationView The current navigation view
359 public NavigationView getNavigationView(int viewId) {
360 return this.mNavigationViews[viewId];
364 * Method that initializes the activity.
366 private void init() {
367 this.mHistory = new ArrayList<History>();
368 this.mChRooted = !FileManagerApplication.isAdvancedMode();
372 * Method that displays a welcome message the first time the user
373 * access the application
375 private void showWelcomeMsg() {
376 boolean firstUse = Preferences.getSharedPreferences().getBoolean(
377 FileManagerSettings.SETTINGS_FIRST_USE.getId(),
378 ((Boolean)FileManagerSettings.SETTINGS_FIRST_USE.getDefaultValue()).booleanValue());
380 //Display the welcome message?
382 AlertDialog dialog = DialogHelper.createAlertDialog(
383 this, R.drawable.ic_launcher, R.string.welcome_title, R.string.welcome_msg);
386 // Don't display again this dialog
388 Preferences.savePreference(
389 FileManagerSettings.SETTINGS_FIRST_USE, Boolean.FALSE, true);
390 } catch (Exception e) {/**NON BLOCK**/}
395 * Method that initializes the titlebar of the activity.
397 private void initTitleActionBar() {
398 //Inflate the view and associate breadcrumb
399 NavigationCustomTitleView title =
400 (NavigationCustomTitleView)getLayoutInflater().inflate(
401 R.layout.navigation_view_customtitle, null, false);
402 title.setOnHistoryListener(this);
403 Breadcrumb breadcrumb = (Breadcrumb)title.findViewById(R.id.breadcrumb_view);
404 int cc = this.mNavigationViews.length;
405 for (int i = 0; i < cc; i++) {
406 this.mNavigationViews[i].setBreadcrumb(breadcrumb);
407 this.mNavigationViews[i].setOnHistoryListener(this);
408 this.mNavigationViews[i].setOnNavigationSelectionChangedListener(this);
409 this.mNavigationViews[i].setOnNavigationOnRequestMenuListener(this);
410 this.mNavigationViews[i].setCustomTitle(title);
413 // Set the free disk space warning level of the breadcrumb widget
414 String fds = Preferences.getSharedPreferences().getString(
415 FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
416 (String)FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
417 breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
419 //Configure the action bar options
420 getActionBar().setBackgroundDrawable(
421 getResources().getDrawable(R.drawable.bg_holo_titlebar));
422 getActionBar().setDisplayOptions(
423 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
424 getActionBar().setCustomView(title);
428 * Method that initializes the statusbar of the activity.
430 private void initStatusActionBar() {
431 //Performs a width calculation of buttons. Buttons exceeds the width
432 //of the action bar should be hidden
433 //This application not use android ActionBar because the application
434 //make uses of the title and bottom areas, and wants to force to show
435 //the overflow button (without care of physical buttons)
436 this.mActionBar = (ViewGroup)findViewById(R.id.navigation_actionbar);
437 this.mActionBar.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
439 public void onLayoutChange(
440 View v, int left, int top, int right, int bottom, int oldLeft,
441 int oldTop, int oldRight, int oldBottom) {
442 //Get the width of the action bar
443 int w = v.getMeasuredWidth();
445 //Wake through children calculation his dimensions
446 int bw = (int)getResources().getDimension(R.dimen.default_buttom_width);
448 final ViewGroup abView = ((ViewGroup)v);
449 int cc = abView.getChildCount();
450 for (int i = 0; i < cc; i++) {
451 View child = abView.getChildAt(i);
452 child.setVisibility(cw + bw > w ? View.INVISIBLE : View.VISIBLE);
458 // Have overflow menu?
459 View overflow = findViewById(R.id.ab_overflow);
460 boolean showOptionsMenu = AndroidHelper.showOptionsMenu(getApplicationContext());
461 overflow.setVisibility(showOptionsMenu ? View.VISIBLE : View.GONE);
462 this.mOptionsAnchorView = showOptionsMenu ? overflow : this.mActionBar;
466 * Method that initializes the selectionbar of the activity.
468 private void initSelectionBar() {
469 this.mSelectionBar = (SelectionView)findViewById(R.id.navigation_selectionbar);
473 * Method that initializes the navigation views of the activity
475 private void initNavigationViews() {
476 //Get the navigation views (wishlist: multiple view; for now only one view)
477 this.mNavigationViews = new NavigationView[1];
478 this.mCurrentNavigationView = 0;
480 this.mNavigationViews[0] = (NavigationView)findViewById(R.id.navigation_view);
481 this.mNavigationViews[0].setId(0);
485 * Method that initializes the navigation.
487 * @param viewId The navigation view identifier where apply the navigation
488 * @param restore Initialize from a restore info
491 void initNavigation(final int viewId, final boolean restore) {
492 final NavigationView navigationView = getNavigationView(viewId);
493 this.mHandler.post(new Runnable() {
496 //Create the default console (from the preferences)
498 Console console = ConsoleBuilder.getConsole(NavigationActivity.this);
499 if (console == null) {
500 throw new ConsoleAllocException("console == null"); //$NON-NLS-1$
502 } catch (Throwable ex) {
503 if (!NavigationActivity.this.mChRooted) {
504 //Show exception and exists
505 Log.e(TAG, getString(R.string.msgs_cant_create_console), ex);
506 // We don't have any console
507 // Show exception and exists
508 DialogHelper.showToast(
509 NavigationActivity.this,
510 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
515 // We are in a trouble (something is not allowing creating the console)
516 // Ask the user to return to advanced console mode with a
517 // non-privileged console, prior to make crash the application
522 //Is necessary navigate?
524 //Load the preference initial directory
526 Preferences.getSharedPreferences().getString(
527 FileManagerSettings.SETTINGS_INITIAL_DIR.getId(),
528 (String)FileManagerSettings.SETTINGS_INITIAL_DIR.getDefaultValue());
529 if (NavigationActivity.this.mChRooted) {
530 // Initial directory is the first external sdcard (sdcard, emmc, usb, ...)
531 StorageVolume[] volumes =
532 StorageHelper.getStorageVolumes(NavigationActivity.this);
533 if (volumes != null && volumes.length > 0) {
534 initialDir = volumes[0].getPath();
538 //Ensure initial is an absolute directory
541 CommandHelper.getAbsolutePath(
542 NavigationActivity.this, initialDir, null);
543 } catch (Throwable e) {
544 Log.e(TAG, "Resolve of initital directory fails", e); //$NON-NLS-1$
547 R.string.msgs_settings_invalid_initial_directory,
549 DialogHelper.showToast(NavigationActivity.this, msg, Toast.LENGTH_SHORT);
550 initialDir = FileHelper.ROOT_DIRECTORY;
553 // Change the current directory to the preference initial directory or the
555 String navigateTo = getIntent().getStringExtra(EXTRA_NAVIGATE_TO);
556 if (navigateTo != null && navigateTo.length() > 0) {
557 navigationView.changeCurrentDir(navigateTo);
559 navigationView.changeCurrentDir(initialDir);
567 * Method that verifies the intent passed to the activity, and checks
568 * if a request is made like Search.
570 * @param intent The intent to check
573 void checkIntent(Intent intent) {
575 if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
576 Intent searchIntent = new Intent(this, SearchActivity.class);
577 searchIntent.setAction(Intent.ACTION_SEARCH);
578 //- SearchActivity.EXTRA_SEARCH_DIRECTORY
579 searchIntent.putExtra(
580 SearchActivity.EXTRA_SEARCH_DIRECTORY,
581 getCurrentNavigationView().getCurrentDir());
582 //- SearchManager.APP_DATA
583 if (intent.getBundleExtra(SearchManager.APP_DATA) != null) {
584 Bundle bundle = new Bundle();
585 bundle.putAll(intent.getBundleExtra(SearchManager.APP_DATA));
586 searchIntent.putExtra(SearchManager.APP_DATA, bundle);
588 //-- SearchManager.QUERY
589 String query = intent.getStringExtra(SearchManager.QUERY);
591 searchIntent.putExtra(SearchManager.QUERY, query);
593 //- android.speech.RecognizerIntent.EXTRA_RESULTS
594 ArrayList<String> extraResults =
595 intent.getStringArrayListExtra(android.speech.RecognizerIntent.EXTRA_RESULTS);
596 if (extraResults != null) {
597 searchIntent.putStringArrayListExtra(
598 android.speech.RecognizerIntent.EXTRA_RESULTS, extraResults);
600 startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH);
604 // Navigate to the requested path
605 String navigateTo = intent.getStringExtra(EXTRA_NAVIGATE_TO);
606 if (navigateTo != null && navigateTo.length() >= 0) {
607 getCurrentNavigationView().changeCurrentDir(navigateTo);
616 public boolean onKeyUp(int keyCode, KeyEvent event) {
617 if (keyCode == KeyEvent.KEYCODE_MENU) {
618 showOverflowPopUp(this.mOptionsAnchorView);
621 if (keyCode == KeyEvent.KEYCODE_BACK) {
622 if (checkBackAction()) {
626 return super.onKeyUp(keyCode, event);
633 public boolean onOptionsItemSelected(MenuItem item) {
634 switch (item.getItemId()) {
635 case android.R.id.home:
636 if ((getActionBar().getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP)
637 == ActionBar.DISPLAY_HOME_AS_UP) {
642 return super.onOptionsItemSelected(item);
647 * Method invoked when an action item is clicked.
649 * @param view The button pushed
651 public void onActionBarItemClick(View view) {
652 switch (view.getId()) {
653 //######################
654 //Navigation Custom Title
655 //######################
656 case R.id.ab_configuration:
657 //Show navigation view configuration toolbar
658 getCurrentNavigationView().getCustomTitle().showConfigurationView();
659 getActionBar().setDisplayHomeAsUpEnabled(true);
660 getActionBar().setHomeButtonEnabled(true);
663 //Hide navigation view configuration toolbar
664 getCurrentNavigationView().getCustomTitle().hideConfigurationView();
667 //######################
669 //######################
670 case R.id.ab_filesystem_info:
671 //Show information of the filesystem
672 MountPoint mp = getCurrentNavigationView().getBreadcrumb().getMountPointInfo();
673 DiskUsage du = getCurrentNavigationView().getBreadcrumb().getDiskUsageInfo();
674 showMountPointInfo(mp, du);
677 //######################
678 //Navigation view options
679 //######################
680 case R.id.ab_sort_mode:
681 showSettingsPopUp(view,
682 Arrays.asList(new FileManagerSettings[]{FileManagerSettings.SETTINGS_SORT_MODE}));
684 case R.id.ab_layout_mode:
685 showSettingsPopUp(view,
687 new FileManagerSettings[]{FileManagerSettings.SETTINGS_LAYOUT_MODE}));
689 case R.id.ab_view_options:
690 // If we are in ChRooted mode, then don't show non-secure items
691 if (this.mChRooted) {
692 showSettingsPopUp(view,
693 Arrays.asList(new FileManagerSettings[]{
694 FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST}));
696 showSettingsPopUp(view,
697 Arrays.asList(new FileManagerSettings[]{
698 FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST,
699 FileManagerSettings.SETTINGS_SHOW_HIDDEN,
700 FileManagerSettings.SETTINGS_SHOW_SYSTEM,
701 FileManagerSettings.SETTINGS_SHOW_SYMLINKS}));
706 //######################
708 //######################
709 case R.id.ab_selection_done:
710 //Show information of the filesystem
711 getCurrentNavigationView().onDeselectAll();
714 //######################
716 //######################
717 case R.id.ab_actions:
718 openActionsDialog(getCurrentNavigationView().getCurrentDir(), true);
721 case R.id.ab_bookmarks:
725 case R.id.ab_history:
733 case R.id.ab_overflow:
734 showOverflowPopUp(view);
746 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
748 switch (requestCode) {
749 case INTENT_REQUEST_BOOKMARK:
750 if (resultCode == RESULT_OK) {
751 FileSystemObject fso =
752 (FileSystemObject)data.
753 getSerializableExtra(EXTRA_BOOKMARK_SELECTION);
756 getCurrentNavigationView().open(fso);
761 case INTENT_REQUEST_HISTORY:
762 if (resultCode == RESULT_OK) {
763 //Change current directory
765 (History)data.getSerializableExtra(EXTRA_HISTORY_ENTRY_SELECTION);
766 navigateToHistory(history);
767 } else if (resultCode == RESULT_CANCELED) {
768 boolean clear = data.getBooleanExtra(EXTRA_HISTORY_CLEAR, false);
775 case INTENT_REQUEST_SEARCH:
776 if (resultCode == RESULT_OK) {
778 FileSystemObject fso =
779 (FileSystemObject)data.
780 getSerializableExtra(EXTRA_SEARCH_ENTRY_SELECTION);
781 SearchInfoParcelable searchInfo =
782 data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
784 //Goto to new directory
785 getCurrentNavigationView().open(fso, searchInfo);
787 } else if (resultCode == RESULT_CANCELED) {
788 SearchInfoParcelable searchInfo =
789 data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
790 if (searchInfo != null && searchInfo.isSuccessNavigation()) {
791 //Navigate to previous history
794 // I don't know is the search view was changed, so do a refresh
795 // of the navigation view
796 getCurrentNavigationView().refresh();
811 public void onNewHistory(HistoryNavigable navigable) {
812 //Recollect information about current status
813 History history = new History(this.mHistory.size(), navigable);
814 this.mHistory.add(history);
815 getActionBar().setDisplayHomeAsUpEnabled(true);
816 getActionBar().setHomeButtonEnabled(true);
823 public void onCheckHistory() {
824 //Need to show HomeUp Button
825 boolean enabled = this.mHistory != null && this.mHistory.size() > 0;
826 getActionBar().setDisplayHomeAsUpEnabled(enabled);
827 getActionBar().setHomeButtonEnabled(enabled);
834 public void onRequestRefresh(Object o) {
835 if (o instanceof FileSystemObject) {
836 // Refresh only the item
837 this.getCurrentNavigationView().refresh((FileSystemObject)o);
838 } else if (o == null) {
840 getCurrentNavigationView().refresh();
842 this.getCurrentNavigationView().onDeselectAll();
849 public void onRequestRemove(Object o) {
850 if (o instanceof FileSystemObject) {
852 this.getCurrentNavigationView().removeItem((FileSystemObject)o);
854 //Remove from history
855 removeFromHistory((FileSystemObject)o);
857 this.getCurrentNavigationView().onDeselectAll();
864 public void onNavigateTo(Object o) {
872 public void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems) {
873 this.mSelectionBar.setSelection(selectedItems);
880 public void onRequestMenu(NavigationView navView, FileSystemObject item) {
881 // Show the actions dialog
882 openActionsDialog(item, false);
886 * Method that shows a popup with a menu associated a {@link FileManagerSettings}.
888 * @param anchor The action button that was pressed
889 * @param settings The array of settings associated with the action button
891 private void showSettingsPopUp(View anchor, List<FileManagerSettings> settings) {
893 final MenuSettingsAdapter adapter = new MenuSettingsAdapter(this, settings);
895 //Create a show the popup menu
896 final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor);
897 popup.setOnItemClickListener(new OnItemClickListener() {
899 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
900 FileManagerSettings setting =
901 ((MenuSettingsAdapter)parent.getAdapter()).getSetting(position);
902 final int value = ((MenuSettingsAdapter)parent.getAdapter()).getId(position);
905 if (setting.compareTo(FileManagerSettings.SETTINGS_LAYOUT_MODE) == 0) {
906 //Need to change the layout
907 getCurrentNavigationView().changeViewMode(
908 NavigationLayoutMode.fromId(value));
911 if (setting.getDefaultValue() instanceof Enum<?>) {
913 Preferences.savePreference(setting, new ObjectIdentifier() {
922 Preferences.getSharedPreferences().
925 ((Boolean)setting.getDefaultValue()).booleanValue());
926 Preferences.savePreference(setting, Boolean.valueOf(!newval), false);
928 getCurrentNavigationView().refresh();
930 } catch (Exception e) {
931 Log.e(TAG, "Error applying navigation option", e); //$NON-NLS-1$
932 NavigationActivity.this.mHandler.post(new Runnable() {
935 DialogHelper.showToast(
936 NavigationActivity.this,
937 R.string.msgs_settings_save_failure, Toast.LENGTH_SHORT);
943 getCurrentNavigationView().getCustomTitle().restoreView();
948 popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
950 public void onDismiss() {
958 * Method that shows a popup with the activity main menu.
960 * @param anchor The action button that was pressed
962 private void showOverflowPopUp(View anchor) {
963 SimpleMenuListAdapter adapter =
964 new HighlightedSimpleMenuListAdapter(this, R.menu.navigation);
965 Menu menu = adapter.getMenu();
966 int cc = this.mActionBar.getChildCount();
967 for (int i = 0, j = this.mActionBar.getChildCount() - 1; i < cc; i++, j--) {
968 View child = this.mActionBar.getChildAt(i);
969 boolean visible = child.getVisibility() == View.VISIBLE;
971 menu.removeItem(menu.getItem(j).getItemId());
975 // Check if console selection is allowed
976 if (this.mChRooted) {
977 menu.removeItem(R.id.mnu_console);
980 final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor);
981 popup.setOnItemClickListener(new OnItemClickListener() {
983 public void onItemClick(
984 final AdapterView<?> parent, final View v, final int position, final long id) {
986 final int itemId = (int)id;
987 NavigationActivity.this.mHandler.post(new Runnable() {
992 case R.id.mnu_settings:
994 Intent settings = new Intent(
995 NavigationActivity.this, SettingsPreferences.class);
996 startActivity(settings);
999 case R.id.mnu_console:
1001 ChooseConsoleDialog dialog =
1002 new ChooseConsoleDialog(NavigationActivity.this);
1006 case R.id.mnu_history:
1012 case R.id.mnu_bookmarks:
1018 case R.id.mnu_search:
1034 * Method that show the information of a filesystem mount point.
1036 * @param mp The mount point info
1037 * @param du The disk usage of the mount point
1039 private void showMountPointInfo(MountPoint mp, DiskUsage du) {
1040 //Has mount point info?
1042 //There is no information
1044 DialogHelper.createWarningDialog(this, 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 to to advanced console
1328 //Show a dialog asking the user
1329 AlertDialog dialog =
1330 DialogHelper.createYesNoDialog(
1331 this, R.string.msgs_change_to_advanced_console_mode,
1332 new DialogInterface.OnClickListener() {
1334 public void onClick(DialogInterface alertDialog, int which) {
1335 if (which == DialogInterface.BUTTON_NEGATIVE) {
1336 // We don't have any console
1337 // Show exception and exists
1338 DialogHelper.showToast(
1339 NavigationActivity.this,
1340 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
1345 // Ok. Now try to change to advanced selection console. Any crash
1346 // here is a fatal error. We won't have any console to operate.
1349 ConsoleBuilder.changeToNonPrivilegedConsole(NavigationActivity.this);
1352 Preferences.savePreference(
1353 FileManagerSettings.SETTINGS_ADVANCE_MODE,
1354 Boolean.TRUE, true);
1355 Preferences.savePreference(
1356 FileManagerSettings.SETTINGS_SUPERUSER_MODE,
1357 Boolean.FALSE, true);
1359 } catch (Exception e) {
1360 //Show exception and exists
1361 Log.e(TAG, getString(R.string.msgs_cant_create_console), e);
1362 DialogHelper.showToast(
1363 NavigationActivity.this,
1364 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
1373 * Method that creates a ChRooted environment, protecting the user to break anything in
1377 void createChRooted() {
1378 // If we are in a ChRooted mode, then do nothing
1379 if (this.mChRooted) return;
1380 this.mChRooted = true;
1382 int cc = this.mNavigationViews.length;
1383 for (int i = 0; i < cc; i++) {
1384 this.mNavigationViews[i].createChRooted();
1387 // Remove the selection
1388 cc = this.mNavigationViews.length;
1389 for (int i = 0; i < cc; i++) {
1390 getCurrentNavigationView().onDeselectAll();
1393 // Remove the history (don't allow to access to previous data)
1398 * Method that exits from a ChRooted
1401 void exitChRooted() {
1402 // If we aren't in a ChRooted mode, then do nothing
1403 if (!this.mChRooted) return;
1404 this.mChRooted = false;
1406 int cc = this.mNavigationViews.length;
1407 for (int i = 0; i < cc; i++) {
1408 this.mNavigationViews[i].exitChRooted();
1413 * Method called when a controlled exit is required
1418 FileManagerApplication.destroyBackgroundConsole();
1419 } catch (Throwable ex) {
1423 ConsoleBuilder.destroyConsole();
1424 } catch (Throwable ex) {