OSDN Git Service

Merge "CMFM: User-defined date/time format" into cm-10.1
[android-x86/packages-apps-CMFileManager.git] / src / com / cyanogenmod / filemanager / activities / NavigationActivity.java
1 /*
2  * Copyright (C) 2012 The CyanogenMod Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.cyanogenmod.filemanager.activities;
18
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.res.Configuration;
29 import android.net.Uri;
30 import android.nfc.NfcAdapter;
31 import android.nfc.NfcEvent;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.Parcelable;
35 import android.os.storage.StorageVolume;
36 import android.util.Log;
37 import android.view.KeyEvent;
38 import android.view.Menu;
39 import android.view.MenuItem;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.widget.AdapterView;
43 import android.widget.AdapterView.OnItemClickListener;
44 import android.widget.ImageView;
45 import android.widget.LinearLayout;
46 import android.widget.ListPopupWindow;
47 import android.widget.PopupWindow;
48 import android.widget.TextView;
49 import android.widget.Toast;
50
51 import com.cyanogenmod.filemanager.FileManagerApplication;
52 import com.cyanogenmod.filemanager.R;
53 import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences;
54 import com.cyanogenmod.filemanager.adapters.HighlightedSimpleMenuListAdapter;
55 import com.cyanogenmod.filemanager.adapters.MenuSettingsAdapter;
56 import com.cyanogenmod.filemanager.adapters.SimpleMenuListAdapter;
57 import com.cyanogenmod.filemanager.console.Console;
58 import com.cyanogenmod.filemanager.console.ConsoleAllocException;
59 import com.cyanogenmod.filemanager.console.ConsoleBuilder;
60 import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
61 import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
62 import com.cyanogenmod.filemanager.listeners.OnHistoryListener;
63 import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
64 import com.cyanogenmod.filemanager.model.DiskUsage;
65 import com.cyanogenmod.filemanager.model.FileSystemObject;
66 import com.cyanogenmod.filemanager.model.History;
67 import com.cyanogenmod.filemanager.model.MountPoint;
68 import com.cyanogenmod.filemanager.parcelables.HistoryNavigable;
69 import com.cyanogenmod.filemanager.parcelables.NavigationViewInfoParcelable;
70 import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
71 import com.cyanogenmod.filemanager.preferences.AccessMode;
72 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
73 import com.cyanogenmod.filemanager.preferences.NavigationLayoutMode;
74 import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
75 import com.cyanogenmod.filemanager.preferences.Preferences;
76 import com.cyanogenmod.filemanager.ui.ThemeManager;
77 import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
78 import com.cyanogenmod.filemanager.ui.dialogs.ActionsDialog;
79 import com.cyanogenmod.filemanager.ui.dialogs.FilesystemInfoDialog;
80 import com.cyanogenmod.filemanager.ui.dialogs.FilesystemInfoDialog.OnMountListener;
81 import com.cyanogenmod.filemanager.ui.widgets.Breadcrumb;
82 import com.cyanogenmod.filemanager.ui.widgets.ButtonItem;
83 import com.cyanogenmod.filemanager.ui.widgets.NavigationCustomTitleView;
84 import com.cyanogenmod.filemanager.ui.widgets.NavigationView;
85 import com.cyanogenmod.filemanager.ui.widgets.NavigationView.OnNavigationRequestMenuListener;
86 import com.cyanogenmod.filemanager.ui.widgets.NavigationView.OnNavigationSelectionChangedListener;
87 import com.cyanogenmod.filemanager.ui.widgets.SelectionView;
88 import com.cyanogenmod.filemanager.util.AndroidHelper;
89 import com.cyanogenmod.filemanager.util.CommandHelper;
90 import com.cyanogenmod.filemanager.util.DialogHelper;
91 import com.cyanogenmod.filemanager.util.ExceptionUtil;
92 import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
93 import com.cyanogenmod.filemanager.util.FileHelper;
94 import com.cyanogenmod.filemanager.util.StorageHelper;
95
96 import java.io.File;
97 import java.io.FileNotFoundException;
98 import java.io.Serializable;
99 import java.util.ArrayList;
100 import java.util.Arrays;
101 import java.util.List;
102
103 /**
104  * The main navigation activity. This activity is the center of the application.
105  * From this the user can navigate, search, make actions.<br/>
106  * This activity is singleTop, so when it is displayed no other activities exists in
107  * the stack.<br/>
108  * This cause an issue with the saved instance of this class, because if another activity
109  * is displayed, and the process is killed, NavigationActivity is started and the saved
110  * instance gets corrupted.<br/>
111  * For this reason the methods {link {@link Activity#onSaveInstanceState(Bundle)} and
112  * {@link Activity#onRestoreInstanceState(Bundle)} are not implemented, and every time
113  * the app is killed, is restarted from his initial state.
114  */
115 public class NavigationActivity extends Activity
116     implements OnHistoryListener, OnRequestRefreshListener,
117     OnNavigationRequestMenuListener, OnNavigationSelectionChangedListener {
118
119     private static final String TAG = "NavigationActivity"; //$NON-NLS-1$
120
121     private static boolean DEBUG = false;
122
123     /**
124      * Intent code for request a bookmark selection.
125      */
126     public static final int INTENT_REQUEST_BOOKMARK = 10001;
127
128     /**
129      * Intent code for request a history selection.
130      */
131     public static final int INTENT_REQUEST_HISTORY = 20001;
132
133     /**
134      * Intent code for request a search.
135      */
136     public static final int INTENT_REQUEST_SEARCH = 30001;
137
138
139     /**
140      * Constant for extra information about selected bookmark.
141      */
142     public static final String EXTRA_BOOKMARK_SELECTION =
143             "extra_bookmark_selection"; //$NON-NLS-1$
144
145     /**
146      * Constant for extra information about selected history entry.
147      */
148     public static final String EXTRA_HISTORY_ENTRY_SELECTION =
149             "extra_history_entry_selection"; //$NON-NLS-1$
150
151     /**
152      * Constant for extra information about clear selection action.
153      */
154     public static final String EXTRA_HISTORY_CLEAR =
155             "extra_history_clear_history"; //$NON-NLS-1$
156
157     /**
158      * Constant for extra information about selected search entry.
159      */
160     public static final String EXTRA_SEARCH_ENTRY_SELECTION =
161             "extra_search_entry_selection"; //$NON-NLS-1$
162
163     /**
164      * Constant for extra information about last search data.
165      */
166     public static final String EXTRA_SEARCH_LAST_SEARCH_DATA =
167             "extra_search_last_search_data"; //$NON-NLS-1$
168
169     /**
170      * Constant for extra information for request a navigation to the passed path.
171      */
172     public static final String EXTRA_NAVIGATE_TO =
173             "extra_navigate_to"; //$NON-NLS-1$
174
175     // The timeout needed to reset the exit status for back button
176     // After this time user need to tap 2 times the back button to
177     // exit, and the toast is shown again after the first tap.
178     private static final int RELEASE_EXIT_CHECK_TIMEOUT = 3500;
179
180     private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
181         @Override
182         public void onReceive(Context context, Intent intent) {
183             if (intent != null) {
184                 if (intent.getAction().compareTo(FileManagerSettings.INTENT_SETTING_CHANGED) == 0) {
185                     // The settings has changed
186                     String key = intent.getStringExtra(FileManagerSettings.EXTRA_SETTING_CHANGED_KEY);
187                     if (key != null) {
188                         // Disk usage warning level
189                         if (key.compareTo(FileManagerSettings.
190                                 SETTINGS_DISK_USAGE_WARNING_LEVEL.getId()) == 0) {
191
192                             // Set the free disk space warning level of the breadcrumb widget
193                             Breadcrumb breadcrumb = getCurrentNavigationView().getBreadcrumb();
194                             String fds = Preferences.getSharedPreferences().getString(
195                                     FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
196                                     (String)FileManagerSettings.
197                                         SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
198                             breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
199                             breadcrumb.updateMountPointInfo();
200                             return;
201                         }
202
203                         // Case sensitive sort
204                         if (key.compareTo(FileManagerSettings.
205                                 SETTINGS_CASE_SENSITIVE_SORT.getId()) == 0) {
206                             getCurrentNavigationView().refresh();
207                             return;
208                         }
209
210                         // Use flinger
211                         if (key.compareTo(FileManagerSettings.
212                                 SETTINGS_USE_FLINGER.getId()) == 0) {
213                             boolean useFlinger =
214                                     Preferences.getSharedPreferences().getBoolean(
215                                             FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
216                                                 ((Boolean)FileManagerSettings.
217                                                         SETTINGS_USE_FLINGER.
218                                                             getDefaultValue()).booleanValue());
219                             getCurrentNavigationView().setUseFlinger(useFlinger);
220                             return;
221                         }
222
223                         // Access mode
224                         if (key.compareTo(FileManagerSettings.
225                                 SETTINGS_ACCESS_MODE.getId()) == 0) {
226                             // Is it necessary to create or exit of the ChRooted?
227                             boolean chRooted =
228                                     FileManagerApplication.
229                                         getAccessMode().compareTo(AccessMode.SAFE) == 0;
230                             if (chRooted != NavigationActivity.this.mChRooted) {
231                                 if (chRooted) {
232                                     createChRooted();
233                                 } else {
234                                     exitChRooted();
235                                 }
236                             }
237                         }
238
239                         // Filetime format mode
240                         if (key.compareTo(FileManagerSettings.
241                                 SETTINGS_FILETIME_FORMAT_MODE.getId()) == 0) {
242                             // Refresh the data
243                             synchronized (FileHelper.DATETIME_SYNC) {
244                                 FileHelper.sReloadDateTimeFormats = true;
245                                 NavigationActivity.this.getCurrentNavigationView().refresh();
246                             }
247                         }
248                     }
249
250                 } else if (intent.getAction().compareTo(
251                         FileManagerSettings.INTENT_FILE_CHANGED) == 0) {
252                     // Retrieve the file that was changed
253                     String file =
254                             intent.getStringExtra(FileManagerSettings.EXTRA_FILE_CHANGED_KEY);
255                     try {
256                         FileSystemObject fso = CommandHelper.getFileInfo(context, file, null);
257                         if (fso != null) {
258                             getCurrentNavigationView().refresh(fso);
259                         }
260                     } catch (Exception e) {
261                         ExceptionUtil.translateException(context, e, true, false);
262                     }
263
264                 } else if (intent.getAction().compareTo(
265                         FileManagerSettings.INTENT_THEME_CHANGED) == 0) {
266                     applyTheme();
267
268                 } else if (intent.getAction().compareTo(Intent.ACTION_TIME_CHANGED) == 0 ||
269                            intent.getAction().compareTo(Intent.ACTION_DATE_CHANGED) == 0 ||
270                            intent.getAction().compareTo(Intent.ACTION_TIMEZONE_CHANGED) == 0) {
271                     // Refresh the data
272                     synchronized (FileHelper.DATETIME_SYNC) {
273                         FileHelper.sReloadDateTimeFormats = true;
274                         NavigationActivity.this.getCurrentNavigationView().refresh();
275                     }
276                 }
277             }
278         }
279     };
280
281     /**
282      * @hide
283      */
284     NavigationView[] mNavigationViews;
285     private List<History> mHistory;
286
287     private int mCurrentNavigationView;
288
289     private ViewGroup mActionBar;
290     private SelectionView mSelectionBar;
291
292     private boolean mExitFlag = false;
293     private long mExitBackTimeout = -1;
294
295     private View mOptionsAnchorView;
296
297     private int mOrientation;
298
299     /**
300      * @hide
301      */
302     boolean mChRooted;
303
304     /**
305      * @hide
306      */
307     Handler mHandler;
308
309     /**
310      * {@inheritDoc}
311      */
312     @Override
313     protected void onCreate(Bundle state) {
314
315         if (DEBUG) {
316             Log.d(TAG, "NavigationActivity.onCreate"); //$NON-NLS-1$
317         }
318
319         // Register the broadcast receiver
320         IntentFilter filter = new IntentFilter();
321         filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED);
322         filter.addAction(FileManagerSettings.INTENT_FILE_CHANGED);
323         filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED);
324         filter.addAction(Intent.ACTION_DATE_CHANGED);
325         filter.addAction(Intent.ACTION_TIME_CHANGED);
326         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
327         registerReceiver(this.mNotificationReceiver, filter);
328
329         //Set the main layout of the activity
330         setContentView(R.layout.navigation);
331
332         //Initialize nfc adapter
333         NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
334         if (mNfcAdapter != null) {
335             mNfcAdapter.setBeamPushUrisCallback(new NfcAdapter.CreateBeamUrisCallback() {
336                 @Override
337                 public Uri[] createBeamUris(NfcEvent event) {
338                     List<FileSystemObject> selectedFiles =
339                             getCurrentNavigationView().getSelectedFiles();
340                     if (selectedFiles.size() > 0) {
341                         List<Uri> fileUri = new ArrayList<Uri>();
342                         for (FileSystemObject f : selectedFiles) {
343                             //Beam ignores folders and system files
344                             if (!FileHelper.isDirectory(f) && !FileHelper.isSystemFile(f)) {
345                                 fileUri.add(Uri.fromFile(new File(f.getFullPath())));
346                             }
347                         }
348                         if (fileUri.size() > 0) {
349                             return fileUri.toArray(new Uri[fileUri.size()]);
350                         }
351                     }
352                     return null;
353                 }
354             }, this);
355         }
356
357         //Initialize activity
358         init();
359
360         //Navigation views
361         initNavigationViews();
362
363         //Initialize action bars
364         initTitleActionBar();
365         initStatusActionBar();
366         initSelectionBar();
367
368         // Adjust layout (only when start on landscape mode)
369         int orientation = getResources().getConfiguration().orientation;
370         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
371             onLayoutChanged();
372         }
373         this.mOrientation = orientation;
374
375         // Apply the theme
376         applyTheme();
377
378         // Show welcome message
379         showWelcomeMsg();
380
381         this.mHandler = new Handler();
382         this.mHandler.post(new Runnable() {
383             @Override
384             public void run() {
385                 //Initialize navigation
386                 int cc = NavigationActivity.this.mNavigationViews.length;
387                 for (int i = 0; i < cc; i++) {
388                     initNavigation(i, false);
389                 }
390
391                 //Check the intent action
392                 checkIntent(getIntent());
393             }
394         });
395
396         //Save state
397         super.onCreate(state);
398     }
399
400     /**
401      * {@inheritDoc}
402      */
403     @Override
404     protected void onNewIntent(Intent intent) {
405         //Initialize navigation
406         initNavigation(this.mCurrentNavigationView, true);
407
408         //Check the intent action
409         checkIntent(intent);
410     }
411
412     /**
413      * {@inheritDoc}
414      */
415     @Override
416     public void onConfigurationChanged(Configuration newConfig) {
417         super.onConfigurationChanged(newConfig);
418         onLayoutChanged();
419     }
420
421     /**
422      * {@inheritDoc}
423      */
424     @Override
425     protected void onDestroy() {
426         if (DEBUG) {
427             Log.d(TAG, "NavigationActivity.onDestroy"); //$NON-NLS-1$
428         }
429
430         // Unregister the receiver
431         try {
432             unregisterReceiver(this.mNotificationReceiver);
433         } catch (Throwable ex) {
434             /**NON BLOCK**/
435         }
436
437         //All destroy. Continue
438         super.onDestroy();
439     }
440
441     /**
442      * Method that returns the current navigation view.
443      *
444      * @return NavigationView The current navigation view
445      */
446     public NavigationView getCurrentNavigationView() {
447         return getNavigationView(this.mCurrentNavigationView);
448     }
449
450     /**
451      * Method that returns the current navigation view.
452      *
453      * @param viewId The view to return
454      * @return NavigationView The current navigation view
455      */
456     public NavigationView getNavigationView(int viewId) {
457         if (this.mNavigationViews == null) return null;
458         return this.mNavigationViews[viewId];
459     }
460
461     /**
462      * Method that initializes the activity.
463      */
464     private void init() {
465         this.mHistory = new ArrayList<History>();
466         this.mChRooted = FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
467     }
468
469     /**
470      * Method that displays a welcome message the first time the user
471      * access the application
472      */
473     private void showWelcomeMsg() {
474         boolean firstUse = Preferences.getSharedPreferences().getBoolean(
475                 FileManagerSettings.SETTINGS_FIRST_USE.getId(),
476                 ((Boolean)FileManagerSettings.SETTINGS_FIRST_USE.getDefaultValue()).booleanValue());
477
478         //Display the welcome message?
479         if (firstUse) {
480             AlertDialog dialog = DialogHelper.createAlertDialog(
481                 this, R.drawable.ic_launcher,
482                 R.string.welcome_title, getString(R.string.welcome_msg), false);
483             DialogHelper.delegateDialogShow(this, dialog);
484
485             // Don't display again this dialog
486             try {
487                 Preferences.savePreference(
488                         FileManagerSettings.SETTINGS_FIRST_USE, Boolean.FALSE, true);
489             } catch (Exception e) {/**NON BLOCK**/}
490         }
491     }
492
493     /**
494      * Method that initializes the titlebar of the activity.
495      */
496     private void initTitleActionBar() {
497         //Inflate the view and associate breadcrumb
498         View titleLayout = getLayoutInflater().inflate(
499                 R.layout.navigation_view_customtitle, null, false);
500         NavigationCustomTitleView title =
501                 (NavigationCustomTitleView)titleLayout.findViewById(R.id.navigation_title_flipper);
502         title.setOnHistoryListener(this);
503         Breadcrumb breadcrumb = (Breadcrumb)title.findViewById(R.id.breadcrumb_view);
504         int cc = this.mNavigationViews.length;
505         for (int i = 0; i < cc; i++) {
506             this.mNavigationViews[i].setBreadcrumb(breadcrumb);
507             this.mNavigationViews[i].setOnHistoryListener(this);
508             this.mNavigationViews[i].setOnNavigationSelectionChangedListener(this);
509             this.mNavigationViews[i].setOnNavigationOnRequestMenuListener(this);
510             this.mNavigationViews[i].setCustomTitle(title);
511         }
512
513         // Set the free disk space warning level of the breadcrumb widget
514         String fds = Preferences.getSharedPreferences().getString(
515                 FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
516                 (String)FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
517         breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
518
519         //Configure the action bar options
520         getActionBar().setBackgroundDrawable(
521                 getResources().getDrawable(R.drawable.bg_holo_titlebar));
522         getActionBar().setDisplayOptions(
523                 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
524         getActionBar().setCustomView(titleLayout);
525     }
526
527     /**
528      * Method that initializes the statusbar of the activity.
529      */
530     private void initStatusActionBar() {
531         //Performs a width calculation of buttons. Buttons exceeds the width
532         //of the action bar should be hidden
533         //This application not use android ActionBar because the application
534         //make uses of the title and bottom areas, and wants to force to show
535         //the overflow button (without care of physical buttons)
536         this.mActionBar = (ViewGroup)findViewById(R.id.navigation_actionbar);
537         this.mActionBar.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
538             @Override
539             public void onLayoutChange(
540                     View v, int left, int top, int right, int bottom, int oldLeft,
541                     int oldTop, int oldRight, int oldBottom) {
542                 //Get the width of the action bar
543                 int w = v.getMeasuredWidth();
544
545                 //Wake through children calculation his dimensions
546                 int bw = (int)getResources().getDimension(R.dimen.default_buttom_width);
547                 int cw = 0;
548                 final ViewGroup abView = ((ViewGroup)v);
549                 int cc = abView.getChildCount();
550                 for (int i = 0; i < cc; i++) {
551                     View child = abView.getChildAt(i);
552                     child.setVisibility(cw + bw > w ? View.INVISIBLE : View.VISIBLE);
553                     cw += bw;
554                 }
555             }
556         });
557
558         // Have overflow menu?
559         View overflow = findViewById(R.id.ab_overflow);
560         boolean showOptionsMenu = AndroidHelper.showOptionsMenu(getApplicationContext());
561         overflow.setVisibility(showOptionsMenu ? View.VISIBLE : View.GONE);
562         this.mOptionsAnchorView = showOptionsMenu ? overflow : this.mActionBar;
563
564         // Show the status bar
565         View statusBar = findViewById(R.id.navigation_statusbar_portrait_holder);
566         statusBar.setVisibility(View.VISIBLE);
567     }
568
569     /**
570      * Method that initializes the selectionbar of the activity.
571      */
572     private void initSelectionBar() {
573         this.mSelectionBar = (SelectionView)findViewById(R.id.navigation_selectionbar);
574     }
575
576     /**
577      * Method that initializes the navigation views of the activity
578      */
579     private void initNavigationViews() {
580         //Get the navigation views (wishlist: multiple view; for now only one view)
581         this.mNavigationViews = new NavigationView[1];
582         this.mCurrentNavigationView = 0;
583         //- 0
584         this.mNavigationViews[0] = (NavigationView)findViewById(R.id.navigation_view);
585         this.mNavigationViews[0].setId(0);
586     }
587
588     /**
589      * Method that initializes the navigation.
590      *
591      * @param viewId The navigation view identifier where apply the navigation
592      * @param restore Initialize from a restore info
593      * @hide
594      */
595     void initNavigation(final int viewId, final boolean restore) {
596         final NavigationView navigationView = getNavigationView(viewId);
597         this.mHandler.post(new Runnable() {
598             @Override
599             public void run() {
600                 //Create the default console (from the preferences)
601                 try {
602                     Console console = ConsoleBuilder.getConsole(NavigationActivity.this);
603                     if (console == null) {
604                         throw new ConsoleAllocException("console == null"); //$NON-NLS-1$
605                     }
606                 } catch (Throwable ex) {
607                     if (!NavigationActivity.this.mChRooted) {
608                         //Show exception and exit
609                         Log.e(TAG, getString(R.string.msgs_cant_create_console), ex);
610                         // We don't have any console
611                         // Show exception and exit
612                         DialogHelper.showToast(
613                                 NavigationActivity.this,
614                                 R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
615                         exit();
616                         return;
617                     }
618
619                     // We are in a trouble (something is not allowing creating the console)
620                     // Ask the user to return to prompt or root access mode mode with a
621                     // non-privileged console, prior to make crash the application
622                     askOrExit();
623                     return;
624                 }
625
626                 //Is necessary navigate?
627                 if (!restore) {
628                     applyInitialDir(navigationView);
629                 }
630             }
631         });
632     }
633
634     /**
635      * Method that applies the user-defined initial directory
636      *
637      * @param navigationView The navigation view
638      * @hide
639      */
640     void applyInitialDir(final NavigationView navigationView) {
641         //Load the user-defined initial directory
642         String initialDir =
643                 Preferences.getSharedPreferences().getString(
644                     FileManagerSettings.SETTINGS_INITIAL_DIR.getId(),
645                     (String)FileManagerSettings.
646                         SETTINGS_INITIAL_DIR.getDefaultValue());
647
648         // Check if request navigation to directory (use as default), and
649         // ensure chrooted and absolute path
650         String navigateTo = getIntent().getStringExtra(EXTRA_NAVIGATE_TO);
651         if (navigateTo != null && navigateTo.length() > 0) {
652             initialDir = navigateTo;
653         }
654
655         if (this.mChRooted) {
656             // Initial directory is the first external sdcard (sdcard, emmc, usb, ...)
657             StorageVolume[] volumes =
658                     StorageHelper.getStorageVolumes(this);
659             if (volumes != null && volumes.length > 0) {
660                 initialDir = volumes[0].getPath();
661                 //Ensure that initial directory is an absolute directory
662                 initialDir = FileHelper.getAbsPath(initialDir);
663             } else {
664                 // Show exception and exit
665                 DialogHelper.showToast(
666                         this,
667                         R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
668                 exit();
669                 return;
670             }
671         } else {
672             //Ensure that initial directory is an absolute directory
673             final String userInitialDir = initialDir;
674             initialDir = FileHelper.getAbsPath(initialDir);
675             final String absInitialDir = initialDir;
676             File f = new File(initialDir);
677             boolean exists = f.exists();
678             if (!exists) {
679                 // Fix for /data/media/0. Libcore doesn't detect it correctly.
680                 try {
681                     exists = CommandHelper.getFileInfo(this, initialDir, false, null) != null;
682                 } catch (InsufficientPermissionsException ipex) {
683                     ExceptionUtil.translateException(
684                             this, ipex, false, true, new OnRelaunchCommandResult() {
685                         @Override
686                         public void onSuccess() {
687                             navigationView.changeCurrentDir(absInitialDir);
688                         }
689                         @Override
690                         public void onFailed(Throwable cause) {
691                             showInitialInvalidDirectoryMsg(userInitialDir);
692                             navigationView.changeCurrentDir(FileHelper.ROOT_DIRECTORY);
693                         }
694                         @Override
695                         public void onCancelled() {
696                             showInitialInvalidDirectoryMsg(userInitialDir);
697                             navigationView.changeCurrentDir(FileHelper.ROOT_DIRECTORY);
698                         }
699                     });
700
701                     // Asynchronous mode
702                     return;
703                 } catch (Exception ex) {
704                     // We are not interested in other exceptions
705                     ExceptionUtil.translateException(this, ex, true, false);
706                 }
707
708                 // Check again the initial directory
709                 if (!exists) {
710                     showInitialInvalidDirectoryMsg(userInitialDir);
711                     initialDir = FileHelper.ROOT_DIRECTORY;
712                 }
713
714                 // Weird, but we have a valid initial directory
715             }
716         }
717
718         // Change the current directory to the user-defined initial directory
719         navigationView.changeCurrentDir(initialDir);
720     }
721
722     /**
723      * Displays a message reporting invalid directory
724      *
725      * @param initialDir The initial directory
726      * @hide
727      */
728     void showInitialInvalidDirectoryMsg(String initialDir) {
729         // Change to root directory
730         DialogHelper.showToast(
731                 this,
732                 getString(
733                         R.string.msgs_settings_invalid_initial_directory,
734                         initialDir),
735                 Toast.LENGTH_SHORT);
736     }
737
738     /**
739      * Method that verifies the intent passed to the activity, and checks
740      * if a request is made like Search.
741      *
742      * @param intent The intent to check
743      * @hide
744      */
745     void checkIntent(Intent intent) {
746         //Search action
747         if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
748             Intent searchIntent = new Intent(this, SearchActivity.class);
749             searchIntent.setAction(Intent.ACTION_SEARCH);
750             //- SearchActivity.EXTRA_SEARCH_DIRECTORY
751             searchIntent.putExtra(
752                     SearchActivity.EXTRA_SEARCH_DIRECTORY,
753                     getCurrentNavigationView().getCurrentDir());
754             //- SearchManager.APP_DATA
755             if (intent.getBundleExtra(SearchManager.APP_DATA) != null) {
756                 Bundle bundle = new Bundle();
757                 bundle.putAll(intent.getBundleExtra(SearchManager.APP_DATA));
758                 searchIntent.putExtra(SearchManager.APP_DATA, bundle);
759             }
760             //-- SearchManager.QUERY
761             String query = intent.getStringExtra(SearchManager.QUERY);
762             if (query != null) {
763                 searchIntent.putExtra(SearchManager.QUERY, query);
764             }
765             //- android.speech.RecognizerIntent.EXTRA_RESULTS
766             ArrayList<String> extraResults =
767                     intent.getStringArrayListExtra(android.speech.RecognizerIntent.EXTRA_RESULTS);
768             if (extraResults != null) {
769                 searchIntent.putStringArrayListExtra(
770                         android.speech.RecognizerIntent.EXTRA_RESULTS, extraResults);
771             }
772             startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH);
773             return;
774         }
775
776         // Navigate to the requested path
777         String navigateTo = intent.getStringExtra(EXTRA_NAVIGATE_TO);
778         if (navigateTo != null && navigateTo.length() >= 0) {
779             getCurrentNavigationView().changeCurrentDir(navigateTo);
780         }
781     }
782
783     /**
784      * {@inheritDoc}
785      */
786     @Override
787     public boolean onKeyUp(int keyCode, KeyEvent event) {
788         if (keyCode == KeyEvent.KEYCODE_MENU) {
789             showOverflowPopUp(this.mOptionsAnchorView);
790             return true;
791         }
792         if (keyCode == KeyEvent.KEYCODE_BACK) {
793             if (checkBackAction()) {
794                 return true;
795             }
796
797             // An exit event has occurred, force the destroy the consoles
798             exit();
799         }
800         return super.onKeyUp(keyCode, event);
801     }
802
803     /**
804      * {@inheritDoc}
805      */
806     @Override
807     public boolean onOptionsItemSelected(MenuItem item) {
808        switch (item.getItemId()) {
809           case android.R.id.home:
810               if ((getActionBar().getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP)
811                       == ActionBar.DISPLAY_HOME_AS_UP) {
812                   checkBackAction();
813               }
814               return true;
815           default:
816              return super.onOptionsItemSelected(item);
817        }
818     }
819
820     /**
821      * Method invoked when an action item is clicked.
822      *
823      * @param view The button pushed
824      */
825     public void onActionBarItemClick(View view) {
826         switch (view.getId()) {
827             //######################
828             //Navigation Custom Title
829             //######################
830             case R.id.ab_configuration:
831                 //Show navigation view configuration toolbar
832                 getCurrentNavigationView().getCustomTitle().showConfigurationView();
833                 getActionBar().setDisplayHomeAsUpEnabled(true);
834                 getActionBar().setHomeButtonEnabled(true);
835                 break;
836             case R.id.ab_close:
837                 //Hide navigation view configuration toolbar
838                 getCurrentNavigationView().getCustomTitle().hideConfigurationView();
839                 break;
840
841             //######################
842             //Breadcrumb Actions
843             //######################
844             case R.id.ab_filesystem_info:
845                 //Show information of the filesystem
846                 MountPoint mp = getCurrentNavigationView().getBreadcrumb().getMountPointInfo();
847                 DiskUsage du = getCurrentNavigationView().getBreadcrumb().getDiskUsageInfo();
848                 showMountPointInfo(mp, du);
849                 break;
850
851             //######################
852             //Navigation view options
853             //######################
854             case R.id.ab_sort_mode:
855                 showSettingsPopUp(view,
856                         Arrays.asList(
857                                 new FileManagerSettings[]{
858                                         FileManagerSettings.SETTINGS_SORT_MODE}));
859                 break;
860             case R.id.ab_layout_mode:
861                 showSettingsPopUp(view,
862                         Arrays.asList(
863                                 new FileManagerSettings[]{
864                                         FileManagerSettings.SETTINGS_LAYOUT_MODE}));
865                 break;
866             case R.id.ab_view_options:
867                 // If we are in ChRooted mode, then don't show non-secure items
868                 if (this.mChRooted) {
869                     showSettingsPopUp(view,
870                             Arrays.asList(new FileManagerSettings[]{
871                                     FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST}));
872                 } else {
873                     showSettingsPopUp(view,
874                             Arrays.asList(new FileManagerSettings[]{
875                                     FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST,
876                                     FileManagerSettings.SETTINGS_SHOW_HIDDEN,
877                                     FileManagerSettings.SETTINGS_SHOW_SYSTEM,
878                                     FileManagerSettings.SETTINGS_SHOW_SYMLINKS}));
879                 }
880
881                 break;
882
883             //######################
884             //Selection Actions
885             //######################
886             case R.id.ab_selection_done:
887                 //Show information of the filesystem
888                 getCurrentNavigationView().onDeselectAll();
889                 break;
890
891             //######################
892             //Action Bar buttons
893             //######################
894             case R.id.ab_actions:
895                 openActionsDialog(getCurrentNavigationView().getCurrentDir(), true);
896                 break;
897
898             case R.id.ab_bookmarks:
899                 openBookmarks();
900                 break;
901
902             case R.id.ab_history:
903                 openHistory();
904                 break;
905
906             case R.id.ab_search:
907                 openSearch();
908                 break;
909
910             case R.id.ab_overflow:
911                 showOverflowPopUp(view);
912                 break;
913
914             default:
915                 break;
916         }
917     }
918
919     /**
920      * {@inheritDoc}
921      */
922     @Override
923     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
924         if (data != null) {
925             switch (requestCode) {
926                 case INTENT_REQUEST_BOOKMARK:
927                     if (resultCode == RESULT_OK) {
928                         FileSystemObject fso =
929                                 (FileSystemObject)data.
930                                     getSerializableExtra(EXTRA_BOOKMARK_SELECTION);
931                         if (fso != null) {
932                             //Open the fso
933                             getCurrentNavigationView().open(fso);
934                         }
935                     }
936                     break;
937
938                 case INTENT_REQUEST_HISTORY:
939                     if (resultCode == RESULT_OK) {
940                         //Change current directory
941                         History history =
942                                 (History)data.getSerializableExtra(EXTRA_HISTORY_ENTRY_SELECTION);
943                         navigateToHistory(history);
944                     } else if (resultCode == RESULT_CANCELED) {
945                         boolean clear = data.getBooleanExtra(EXTRA_HISTORY_CLEAR, false);
946                         if (clear) {
947                             clearHistory();
948                         }
949                     }
950                     break;
951
952                 case INTENT_REQUEST_SEARCH:
953                     if (resultCode == RESULT_OK) {
954                         //Change directory?
955                         FileSystemObject fso =
956                                 (FileSystemObject)data.
957                                     getSerializableExtra(EXTRA_SEARCH_ENTRY_SELECTION);
958                         SearchInfoParcelable searchInfo =
959                                 data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
960                         if (fso != null) {
961                             //Goto to new directory
962                             getCurrentNavigationView().open(fso, searchInfo);
963                         }
964                     } else if (resultCode == RESULT_CANCELED) {
965                         SearchInfoParcelable searchInfo =
966                                 data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
967                         if (searchInfo != null && searchInfo.isSuccessNavigation()) {
968                             //Navigate to previous history
969                             back();
970                         } else {
971                             // I don't know is the search view was changed, so try to do a refresh
972                             // of the navigation view
973                             getCurrentNavigationView().refresh(true);
974                         }
975                     }
976                     break;
977
978                 default:
979                     break;
980             }
981         }
982     }
983
984     /**
985      * {@inheritDoc}
986      */
987     @Override
988     public void onNewHistory(HistoryNavigable navigable) {
989         //Recollect information about current status
990         History history = new History(this.mHistory.size(), navigable);
991         this.mHistory.add(history);
992         getActionBar().setDisplayHomeAsUpEnabled(true);
993         getActionBar().setHomeButtonEnabled(true);
994     }
995
996     /**
997      * {@inheritDoc}
998      */
999     @Override
1000     public void onCheckHistory() {
1001         //Need to show HomeUp Button
1002         boolean enabled = this.mHistory != null && this.mHistory.size() > 0;
1003         getActionBar().setDisplayHomeAsUpEnabled(enabled);
1004         getActionBar().setHomeButtonEnabled(enabled);
1005     }
1006
1007     /**
1008      * {@inheritDoc}
1009      */
1010     @Override
1011     public void onRequestRefresh(Object o, boolean clearSelection) {
1012         if (o instanceof FileSystemObject) {
1013             // Refresh only the item
1014             this.getCurrentNavigationView().refresh((FileSystemObject)o);
1015         } else if (o == null) {
1016             // Refresh all
1017             getCurrentNavigationView().refresh();
1018         }
1019         if (clearSelection) {
1020             this.getCurrentNavigationView().onDeselectAll();
1021         }
1022     }
1023
1024     /**
1025      * {@inheritDoc}
1026      */
1027     @Override
1028     public void onRequestRemove(Object o, boolean clearSelection) {
1029         if (o instanceof FileSystemObject) {
1030             // Remove from view
1031             this.getCurrentNavigationView().removeItem((FileSystemObject)o);
1032
1033             //Remove from history
1034             removeFromHistory((FileSystemObject)o);
1035         } else {
1036             onRequestRefresh(null, clearSelection);
1037         }
1038         if (clearSelection) {
1039             this.getCurrentNavigationView().onDeselectAll();
1040         }
1041     }
1042
1043     /**
1044      * {@inheritDoc}
1045      */
1046     @Override
1047     public void onNavigateTo(Object o) {
1048         // Ignored
1049     }
1050
1051     /**
1052      * {@inheritDoc}
1053      */
1054     @Override
1055     public void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems) {
1056         this.mSelectionBar.setSelection(selectedItems);
1057     }
1058
1059     /**
1060      * {@inheritDoc}
1061      */
1062     @Override
1063     public void onRequestMenu(NavigationView navView, FileSystemObject item) {
1064         // Show the actions dialog
1065         openActionsDialog(item, false);
1066     }
1067
1068     /**
1069      * Method that shows a popup with a menu associated a {@link FileManagerSettings}.
1070      *
1071      * @param anchor The action button that was pressed
1072      * @param settings The array of settings associated with the action button
1073      */
1074     private void showSettingsPopUp(View anchor, List<FileManagerSettings> settings) {
1075         //Create the adapter
1076         final MenuSettingsAdapter adapter = new MenuSettingsAdapter(this, settings);
1077
1078         //Create a show the popup menu
1079         final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor);
1080         popup.setOnItemClickListener(new OnItemClickListener() {
1081             @Override
1082             public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
1083                 FileManagerSettings setting =
1084                         ((MenuSettingsAdapter)parent.getAdapter()).getSetting(position);
1085                 final int value = ((MenuSettingsAdapter)parent.getAdapter()).getId(position);
1086                 popup.dismiss();
1087                 try {
1088                     if (setting.compareTo(FileManagerSettings.SETTINGS_LAYOUT_MODE) == 0) {
1089                         //Need to change the layout
1090                         getCurrentNavigationView().changeViewMode(
1091                                 NavigationLayoutMode.fromId(value));
1092                     } else {
1093                         //Save and refresh
1094                         if (setting.getDefaultValue() instanceof Enum<?>) {
1095                             //Enumeration
1096                             Preferences.savePreference(setting, new ObjectIdentifier() {
1097                                 @Override
1098                                 public int getId() {
1099                                     return value;
1100                                 }
1101                             }, false);
1102                         } else {
1103                             //Boolean
1104                             boolean newval =
1105                                     Preferences.getSharedPreferences().
1106                                         getBoolean(
1107                                             setting.getId(),
1108                                             ((Boolean)setting.getDefaultValue()).booleanValue());
1109                             Preferences.savePreference(setting, Boolean.valueOf(!newval), false);
1110                         }
1111                         getCurrentNavigationView().refresh();
1112                     }
1113                 } catch (Exception e) {
1114                     Log.e(TAG, "Error applying navigation option", e); //$NON-NLS-1$
1115                     NavigationActivity.this.mHandler.post(new Runnable() {
1116                         @Override
1117                         public void run() {
1118                             DialogHelper.showToast(
1119                                     NavigationActivity.this,
1120                                     R.string.msgs_settings_save_failure, Toast.LENGTH_SHORT);
1121                         }
1122                     });
1123
1124                 } finally {
1125                     adapter.dispose();
1126                     getCurrentNavigationView().getCustomTitle().restoreView();
1127                 }
1128
1129             }
1130         });
1131         popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
1132             @Override
1133             public void onDismiss() {
1134                 adapter.dispose();
1135             }
1136         });
1137         popup.show();
1138     }
1139
1140     /**
1141      * Method that shows a popup with the activity main menu.
1142      *
1143      * @param anchor The action button that was pressed
1144      */
1145     private void showOverflowPopUp(View anchor) {
1146         SimpleMenuListAdapter adapter =
1147                 new HighlightedSimpleMenuListAdapter(this, R.menu.navigation);
1148         Menu menu = adapter.getMenu();
1149         int cc = this.mActionBar.getChildCount();
1150         for (int i = 0, j = this.mActionBar.getChildCount() - 1; i < cc; i++, j--) {
1151             View child = this.mActionBar.getChildAt(i);
1152             boolean visible = child.getVisibility() == View.VISIBLE;
1153             if (visible) {
1154                 menu.removeItem(menu.getItem(j).getItemId());
1155             }
1156         }
1157
1158         final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor);
1159         popup.setOnItemClickListener(new OnItemClickListener() {
1160             @Override
1161             public void onItemClick(
1162                     final AdapterView<?> parent, final View v, final int position, final long id) {
1163
1164                 final int itemId = (int)id;
1165                 NavigationActivity.this.mHandler.post(new Runnable() {
1166                     @Override
1167                     public void run() {
1168                         popup.dismiss();
1169                         switch (itemId) {
1170                             case R.id.mnu_settings:
1171                                 //Settings
1172                                 Intent settings = new Intent(
1173                                         NavigationActivity.this, SettingsPreferences.class);
1174                                 startActivity(settings);
1175                                 break;
1176
1177                             case R.id.mnu_history:
1178                                 //History
1179                                 openHistory();
1180                                 popup.dismiss();
1181                                 break;
1182
1183                             case R.id.mnu_bookmarks:
1184                                 //Bookmarks
1185                                 openBookmarks();
1186                                 popup.dismiss();
1187                                 break;
1188
1189                             case R.id.mnu_search:
1190                                 //Search
1191                                 openSearch();
1192                                 popup.dismiss();
1193                                 break;
1194                             default:
1195                                 break;
1196                         }
1197                     }
1198                 });
1199             }
1200         });
1201         popup.show();
1202     }
1203
1204     /**
1205      * Method that show the information of a filesystem mount point.
1206      *
1207      * @param mp The mount point info
1208      * @param du The disk usage of the mount point
1209      */
1210     private void showMountPointInfo(MountPoint mp, DiskUsage du) {
1211         //Has mount point info?
1212         if (mp == null) {
1213             //There is no information
1214             AlertDialog alert =
1215                     DialogHelper.createWarningDialog(
1216                             this,
1217                             R.string.filesystem_info_warning_title,
1218                             R.string.filesystem_info_warning_msg);
1219             DialogHelper.delegateDialogShow(this, alert);
1220             return;
1221         }
1222
1223         //Show a the filesystem info dialog
1224         FilesystemInfoDialog dialog = new FilesystemInfoDialog(this, mp, du);
1225         dialog.setOnMountListener(new OnMountListener() {
1226             @Override
1227             public void onRemount(MountPoint mountPoint) {
1228                 //Update the statistics of breadcrumb, only if mount point is the same
1229                 Breadcrumb breadcrumb = getCurrentNavigationView().getBreadcrumb();
1230                 if (breadcrumb.getMountPointInfo().compareTo(mountPoint) == 0) {
1231                     breadcrumb.updateMountPointInfo();
1232                 }
1233             }
1234         });
1235         dialog.show();
1236     }
1237
1238     /**
1239      * Method that checks the action that must be realized when the
1240      * back button is pushed.
1241      *
1242      * @return boolean Indicates if the action must be intercepted
1243      */
1244     private boolean checkBackAction() {
1245         // We need a basic structure to check this
1246         if (getCurrentNavigationView() == null) return false;
1247
1248         //Check if the configuration view is showing. In this case back
1249         //action must be "close configuration"
1250         if (getCurrentNavigationView().getCustomTitle().isConfigurationViewShowing()) {
1251             getCurrentNavigationView().getCustomTitle().restoreView();
1252             return true;
1253         }
1254
1255         //Do back operation over the navigation history
1256         boolean flag = this.mExitFlag;
1257
1258         this.mExitFlag = !back();
1259
1260         // Retrieve if the exit status timeout has expired
1261         long now = System.currentTimeMillis();
1262         boolean timeout = (this.mExitBackTimeout == -1 ||
1263                             (now - this.mExitBackTimeout) > RELEASE_EXIT_CHECK_TIMEOUT);
1264
1265         //Check if there no history and if the user was advised in the last back action
1266         if (this.mExitFlag && (this.mExitFlag != flag || timeout)) {
1267             //Communicate the user that the next time the application will be closed
1268             this.mExitBackTimeout = System.currentTimeMillis();
1269             DialogHelper.showToast(this, R.string.msgs_push_again_to_exit, Toast.LENGTH_SHORT);
1270             return true;
1271         }
1272
1273         //Back action not applied
1274         return !this.mExitFlag;
1275     }
1276
1277     /**
1278      * {@inheritDoc}
1279      */
1280     @Override
1281     public boolean onSearchRequested() {
1282         Bundle bundle = new Bundle();
1283         bundle.putString(
1284                 SearchActivity.EXTRA_SEARCH_DIRECTORY,
1285                 getCurrentNavigationView().getCurrentDir());
1286         startSearch(Preferences.getLastSearch(), true, bundle, false);
1287         return true;
1288     }
1289
1290     /**
1291      * Method that returns the history size.
1292      */
1293     private void clearHistory() {
1294         this.mHistory.clear();
1295         onCheckHistory();
1296     }
1297
1298     /**
1299      * Method that navigates to the passed history reference.
1300      *
1301      * @param history The history reference
1302      * @return boolean A problem occurs while navigate
1303      */
1304     public boolean navigateToHistory(History history) {
1305         try {
1306             //Gets the history
1307             History realHistory = this.mHistory.get(history.getPosition());
1308
1309             //Navigate to item. Check what kind of history is
1310             if (realHistory.getItem() instanceof NavigationViewInfoParcelable) {
1311                 //Navigation
1312                 NavigationViewInfoParcelable info =
1313                         (NavigationViewInfoParcelable)realHistory.getItem();
1314                 int viewId = info.getId();
1315                 NavigationView view = getNavigationView(viewId);
1316                 // Selected items must not be restored from on history navigation
1317                 info.setSelectedFiles(view.getSelectedFiles());
1318                 view.onRestoreState(info);
1319
1320             } else if (realHistory.getItem() instanceof SearchInfoParcelable) {
1321                 //Search (open search with the search results)
1322                 SearchInfoParcelable info = (SearchInfoParcelable)realHistory.getItem();
1323                 Intent searchIntent = new Intent(this, SearchActivity.class);
1324                 searchIntent.setAction(SearchActivity.ACTION_RESTORE);
1325                 searchIntent.putExtra(SearchActivity.EXTRA_SEARCH_RESTORE, (Parcelable)info);
1326                 startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH);
1327             } else {
1328                 //The type is unknown
1329                 throw new IllegalArgumentException("Unknown history type"); //$NON-NLS-1$
1330             }
1331
1332             //Remove the old history
1333             int cc = realHistory.getPosition();
1334             for (int i = this.mHistory.size() - 1; i >= cc; i--) {
1335                 this.mHistory.remove(i);
1336             }
1337             if (this.mHistory.size() == 0) {
1338                 getActionBar().setDisplayHomeAsUpEnabled(false);
1339                 getActionBar().setHomeButtonEnabled(false);
1340             }
1341
1342             //Navigate
1343             return true;
1344
1345         } catch (Throwable ex) {
1346             if (history != null) {
1347                 Log.e(TAG,
1348                         String.format("Failed to navigate to history %d: %s", //$NON-NLS-1$
1349                                 Integer.valueOf(history.getPosition()),
1350                                 history.getItem().getTitle()), ex);
1351             } else {
1352                 Log.e(TAG,
1353                         String.format("Failed to navigate to history: null", ex)); //$NON-NLS-1$
1354             }
1355             this.mHandler.post(new Runnable() {
1356                 @Override
1357                 public void run() {
1358                     DialogHelper.showToast(
1359                             NavigationActivity.this,
1360                             R.string.msgs_history_unknown, Toast.LENGTH_LONG);
1361                 }
1362             });
1363
1364             //Not change directory
1365             return false;
1366         }
1367     }
1368
1369     /**
1370      * Method that request a back action over the navigation history.
1371      *
1372      * @return boolean If a back action was applied
1373      */
1374     public boolean back() {
1375         // Check that has valid history
1376         while (this.mHistory.size() > 0) {
1377             History h = this.mHistory.get(this.mHistory.size() - 1);
1378             if (h.getItem() instanceof NavigationViewInfoParcelable) {
1379                 // Verify that the path exists
1380                 String path = ((NavigationViewInfoParcelable)h.getItem()).getCurrentDir();
1381
1382                 try {
1383                     FileSystemObject info = CommandHelper.getFileInfo(this, path, null);
1384                     if (info != null) {
1385                         break;
1386                     }
1387                     this.mHistory.remove(this.mHistory.size() - 1);
1388                 } catch (Exception e) {
1389                     ExceptionUtil.translateException(this, e, true, false);
1390                     this.mHistory.remove(this.mHistory.size() - 1);
1391                 }
1392             } else {
1393                 break;
1394             }
1395         }
1396
1397         //Extract a history from the
1398         if (this.mHistory.size() > 0) {
1399             //Navigate to history
1400             return navigateToHistory(this.mHistory.get(this.mHistory.size() - 1));
1401         }
1402
1403         //Nothing to apply
1404         return false;
1405     }
1406
1407     /**
1408      * Method that opens the actions dialog
1409      *
1410      * @param item The path or the {@link FileSystemObject}
1411      * @param global If the menu to display is the one with global actions
1412      */
1413     private void openActionsDialog(Object item, boolean global) {
1414         // Resolve the full path
1415         String path = String.valueOf(item);
1416         if (item instanceof FileSystemObject) {
1417             path = ((FileSystemObject)item).getFullPath();
1418         }
1419
1420         // Prior to show the dialog, refresh the item reference
1421         FileSystemObject fso = null;
1422         try {
1423             fso = CommandHelper.getFileInfo(this, path, false, null);
1424             if (fso == null) {
1425                 throw new NoSuchFileOrDirectory(path);
1426             }
1427
1428         } catch (Exception e) {
1429             // Notify the user
1430             ExceptionUtil.translateException(this, e);
1431
1432             // Remove the object
1433             if (e instanceof FileNotFoundException || e instanceof NoSuchFileOrDirectory) {
1434                 // If have a FileSystemObject reference then there is no need to search
1435                 // the path (less resources used)
1436                 if (item instanceof FileSystemObject) {
1437                     getCurrentNavigationView().removeItem((FileSystemObject)item);
1438                 } else {
1439                     getCurrentNavigationView().removeItem((String)item);
1440                 }
1441             }
1442             return;
1443         }
1444
1445         // Show the dialog
1446         ActionsDialog dialog = new ActionsDialog(this, fso, global, false);
1447         dialog.setOnRequestRefreshListener(this);
1448         dialog.setOnSelectionListener(getCurrentNavigationView());
1449         dialog.show();
1450     }
1451
1452     /**
1453      * Method that opens the bookmarks activity.
1454      * @hide
1455      */
1456     void openBookmarks() {
1457         Intent bookmarksIntent = new Intent(this, BookmarksActivity.class);
1458         bookmarksIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
1459         startActivityForResult(bookmarksIntent, INTENT_REQUEST_BOOKMARK);
1460     }
1461
1462     /**
1463      * Method that opens the history activity.
1464      * @hide
1465      */
1466     void openHistory() {
1467         Intent historyIntent = new Intent(this, HistoryActivity.class);
1468         historyIntent.putExtra(HistoryActivity.EXTRA_HISTORY_LIST, (Serializable)this.mHistory);
1469         historyIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
1470         startActivityForResult(historyIntent, INTENT_REQUEST_HISTORY);
1471     }
1472
1473     /**
1474      * Method that opens the search activity.
1475      * @hide
1476      */
1477     void openSearch() {
1478         onSearchRequested();
1479     }
1480
1481     /**
1482      * Method that remove the {@link FileSystemObject} from the history
1483      */
1484     private void removeFromHistory(FileSystemObject fso) {
1485         if (this.mHistory != null) {
1486             int cc = this.mHistory.size();
1487             for (int i = cc-1; i >= 0 ; i--) {
1488                 History history = this.mHistory.get(i);
1489                 if (history.getItem() instanceof NavigationViewInfoParcelable) {
1490                     String p0 = fso.getFullPath();
1491                     String p1 =
1492                             ((NavigationViewInfoParcelable)history.getItem()).getCurrentDir();
1493                     if (p0.compareTo(p1) == 0) {
1494                         this.mHistory.remove(i);
1495                     }
1496                 }
1497             }
1498         }
1499     }
1500
1501     /**
1502      * Method that ask the user to change the access mode prior to crash.
1503      * @hide
1504      */
1505     void askOrExit() {
1506         //Show a dialog asking the user
1507         AlertDialog dialog =
1508             DialogHelper.createYesNoDialog(
1509                 this,
1510                 R.string.msgs_change_to_prompt_access_mode_title,
1511                 R.string.msgs_change_to_prompt_access_mode_msg,
1512                 new DialogInterface.OnClickListener() {
1513                     @Override
1514                     public void onClick(DialogInterface alertDialog, int which) {
1515                         if (which == DialogInterface.BUTTON_NEGATIVE) {
1516                             // We don't have any console
1517                             // Show exception and exit
1518                             DialogHelper.showToast(
1519                                     NavigationActivity.this,
1520                                     R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
1521                             exit();
1522                             return;
1523                         }
1524
1525                         // Ok. Now try to change to prompt mode. Any crash
1526                         // here is a fatal error. We won't have any console to operate.
1527                         try {
1528                             // Change console
1529                             ConsoleBuilder.changeToNonPrivilegedConsole(NavigationActivity.this);
1530
1531                             // Save preferences
1532                             Preferences.savePreference(
1533                                     FileManagerSettings.SETTINGS_ACCESS_MODE,
1534                                     AccessMode.PROMPT, true);
1535
1536                         } catch (Exception e) {
1537                             // Displays an exception and exit
1538                             Log.e(TAG, getString(R.string.msgs_cant_create_console), e);
1539                             DialogHelper.showToast(
1540                                     NavigationActivity.this,
1541                                     R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
1542                             exit();
1543                         }
1544                     }
1545                });
1546         DialogHelper.delegateDialogShow(this, dialog);
1547     }
1548
1549     /**
1550      * Method that creates a ChRooted environment, protecting the user to break anything in
1551      * the device
1552      * @hide
1553      */
1554     void createChRooted() {
1555         // If we are in a ChRooted mode, then do nothing
1556         if (this.mChRooted) return;
1557         this.mChRooted = true;
1558
1559         int cc = this.mNavigationViews.length;
1560         for (int i = 0; i < cc; i++) {
1561             this.mNavigationViews[i].createChRooted();
1562         }
1563
1564         // Remove the selection
1565         cc = this.mNavigationViews.length;
1566         for (int i = 0; i < cc; i++) {
1567             getCurrentNavigationView().onDeselectAll();
1568         }
1569
1570         // Remove the history (don't allow to access to previous data)
1571         clearHistory();
1572     }
1573
1574     /**
1575      * Method that exits from a ChRooted
1576      * @hide
1577      */
1578     void exitChRooted() {
1579         // If we aren't in a ChRooted mode, then do nothing
1580         if (!this.mChRooted) return;
1581         this.mChRooted = false;
1582
1583         int cc = this.mNavigationViews.length;
1584         for (int i = 0; i < cc; i++) {
1585             this.mNavigationViews[i].exitChRooted();
1586         }
1587     }
1588
1589     /**
1590      * Method called when a controlled exit is required
1591      * @hide
1592      */
1593     void exit() {
1594         try {
1595             FileManagerApplication.destroyBackgroundConsole();
1596         } catch (Throwable ex) {
1597             /**NON BLOCK**/
1598         }
1599         try {
1600             ConsoleBuilder.destroyConsole();
1601         } catch (Throwable ex) {
1602             /**NON BLOCK**/
1603         }
1604         finish();
1605     }
1606
1607     /**
1608      * Method that reconfigures the layout for better fit in portrait and landscape modes
1609      */
1610     private void onLayoutChanged() {
1611         Theme theme = ThemeManager.getCurrentTheme(this);
1612
1613         // Apply only when the orientation was changed
1614         int orientation = getResources().getConfiguration().orientation;
1615         if (this.mOrientation == orientation) return;
1616         this.mOrientation = orientation;
1617
1618         if (this.mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
1619             // Landscape mode
1620             ViewGroup statusBar = (ViewGroup)findViewById(R.id.navigation_statusbar);
1621             if (statusBar.getParent() != null) {
1622                 ViewGroup parent = (ViewGroup) statusBar.getParent();
1623                 parent.removeView(statusBar);
1624             }
1625
1626             // Calculate the action button size (all the buttons must fit in the title bar)
1627             int bw = (int)getResources().getDimension(R.dimen.default_buttom_width);
1628             int abw = this.mActionBar.getChildCount() * bw;
1629             int rbw = 0;
1630             int cc = statusBar.getChildCount();
1631             for (int i = 0; i < cc; i++) {
1632                 View child = statusBar.getChildAt(i);
1633                 if (child instanceof ButtonItem) {
1634                     rbw += bw;
1635                 }
1636             }
1637             int w = abw + rbw;
1638             boolean showOptionsMenu = AndroidHelper.showOptionsMenu(getApplicationContext());
1639             if (!showOptionsMenu) {
1640                 w -= bw;
1641             }
1642
1643             // Add to the new location
1644             ViewGroup newParent = (ViewGroup)findViewById(R.id.navigation_title_landscape_holder);
1645             LinearLayout.LayoutParams params =
1646                     new LinearLayout.LayoutParams(
1647                             w,
1648                             ViewGroup.LayoutParams.MATCH_PARENT);
1649             statusBar.setLayoutParams(params);
1650             newParent.addView(statusBar);
1651
1652             // Apply theme
1653             theme.setBackgroundDrawable(this, statusBar, "titlebar_drawable"); //$NON-NLS-1$
1654
1655             // Hide holder
1656             View holder = findViewById(R.id.navigation_statusbar_portrait_holder);
1657             holder.setVisibility(View.GONE);
1658
1659         } else {
1660             // Portrait mode
1661             ViewGroup statusBar = (ViewGroup)findViewById(R.id.navigation_statusbar);
1662             if (statusBar.getParent() != null) {
1663                 ViewGroup parent = (ViewGroup) statusBar.getParent();
1664                 parent.removeView(statusBar);
1665             }
1666
1667             // Add to the new location
1668             ViewGroup newParent = (ViewGroup)findViewById(
1669                     R.id.navigation_statusbar_portrait_holder);
1670             LinearLayout.LayoutParams params =
1671                     new LinearLayout.LayoutParams(
1672                             ViewGroup.LayoutParams.MATCH_PARENT,
1673                             ViewGroup.LayoutParams.MATCH_PARENT);
1674             statusBar.setLayoutParams(params);
1675             newParent.addView(statusBar);
1676
1677             // Apply theme
1678             theme.setBackgroundDrawable(this, statusBar, "statusbar_drawable"); //$NON-NLS-1$
1679
1680             // Show holder
1681             newParent.setVisibility(View.VISIBLE);
1682         }
1683     }
1684
1685     /**
1686      * Method that applies the current theme to the activity
1687      * @hide
1688      */
1689     void applyTheme() {
1690         int orientation = getResources().getConfiguration().orientation;
1691         Theme theme = ThemeManager.getCurrentTheme(this);
1692         theme.setBaseTheme(this, false);
1693
1694         //- Layout
1695         View v = findViewById(R.id.navigation_layout);
1696         theme.setBackgroundDrawable(this, v, "background_drawable"); //$NON-NLS-1$
1697         //- ActionBar
1698         theme.setTitlebarDrawable(this, getActionBar(), "titlebar_drawable"); //$NON-NLS-1$
1699         //- StatusBar
1700         v = findViewById(R.id.navigation_statusbar);
1701         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
1702             theme.setBackgroundDrawable(this, v, "titlebar_drawable"); //$NON-NLS-1$
1703         } else {
1704             theme.setBackgroundDrawable(this, v, "statusbar_drawable"); //$NON-NLS-1$
1705         }
1706         v = findViewById(R.id.ab_overflow);
1707         theme.setImageDrawable(this, (ImageView)v, "ab_overflow_drawable"); //$NON-NLS-1$
1708         v = findViewById(R.id.ab_actions);
1709         theme.setImageDrawable(this, (ImageView)v, "ab_actions_drawable"); //$NON-NLS-1$
1710         v = findViewById(R.id.ab_search);
1711         theme.setImageDrawable(this, (ImageView)v, "ab_search_drawable"); //$NON-NLS-1$
1712         v = findViewById(R.id.ab_bookmarks);
1713         theme.setImageDrawable(this, (ImageView)v, "ab_bookmarks_drawable"); //$NON-NLS-1$
1714         v = findViewById(R.id.ab_history);
1715         theme.setImageDrawable(this, (ImageView)v, "ab_history_drawable"); //$NON-NLS-1$
1716         //- Expanders
1717         v = findViewById(R.id.ab_configuration);
1718         theme.setImageDrawable(this, (ImageView)v, "expander_open_drawable"); //$NON-NLS-1$
1719         v = findViewById(R.id.ab_close);
1720         theme.setImageDrawable(this, (ImageView)v, "expander_close_drawable"); //$NON-NLS-1$
1721         v = findViewById(R.id.ab_sort_mode);
1722         theme.setImageDrawable(this, (ImageView)v, "ab_sort_mode_drawable"); //$NON-NLS-1$
1723         v = findViewById(R.id.ab_layout_mode);
1724         theme.setImageDrawable(this, (ImageView)v, "ab_layout_mode_drawable"); //$NON-NLS-1$
1725         v = findViewById(R.id.ab_view_options);
1726         theme.setImageDrawable(this, (ImageView)v, "ab_view_options_drawable"); //$NON-NLS-1$
1727         //- SelectionBar
1728         v = findViewById(R.id.navigation_selectionbar);
1729         theme.setBackgroundDrawable(this, v, "selectionbar_drawable"); //$NON-NLS-1$
1730         v = findViewById(R.id.ab_selection_done);
1731         theme.setImageDrawable(this, (ImageView)v, "ab_selection_done_drawable"); //$NON-NLS-1$
1732         v = findViewById(R.id.navigation_status_selection_label);
1733         theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
1734         //- NavigationView
1735         int cc = this.mNavigationViews.length;
1736         for (int i = 0; i < cc; i++) {
1737             getNavigationView(i).applyTheme();
1738         }
1739     }
1740
1741 }