/* * Copyright (C) 2012 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.cyanogenmod.filemanager.activities; import android.app.ActionBar; import android.app.Activity; import android.app.AlertDialog; import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.net.Uri; import android.nfc.NfcAdapter; import android.nfc.NfcEvent; import android.os.Bundle; import android.os.Handler; import android.os.Parcelable; import android.os.storage.StorageVolume; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListPopupWindow; import android.widget.PopupWindow; import android.widget.TextView; import android.widget.Toast; import com.cyanogenmod.filemanager.FileManagerApplication; import com.cyanogenmod.filemanager.R; import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences; import com.cyanogenmod.filemanager.adapters.HighlightedSimpleMenuListAdapter; import com.cyanogenmod.filemanager.adapters.MenuSettingsAdapter; import com.cyanogenmod.filemanager.adapters.SimpleMenuListAdapter; import com.cyanogenmod.filemanager.console.Console; import com.cyanogenmod.filemanager.console.ConsoleAllocException; import com.cyanogenmod.filemanager.console.ConsoleBuilder; import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory; import com.cyanogenmod.filemanager.listeners.OnHistoryListener; import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener; import com.cyanogenmod.filemanager.model.DiskUsage; import com.cyanogenmod.filemanager.model.FileSystemObject; import com.cyanogenmod.filemanager.model.History; import com.cyanogenmod.filemanager.model.MountPoint; import com.cyanogenmod.filemanager.parcelables.HistoryNavigable; import com.cyanogenmod.filemanager.parcelables.NavigationViewInfoParcelable; import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable; import com.cyanogenmod.filemanager.preferences.AccessMode; import com.cyanogenmod.filemanager.preferences.FileManagerSettings; import com.cyanogenmod.filemanager.preferences.NavigationLayoutMode; import com.cyanogenmod.filemanager.preferences.ObjectIdentifier; import com.cyanogenmod.filemanager.preferences.Preferences; import com.cyanogenmod.filemanager.ui.ThemeManager; import com.cyanogenmod.filemanager.ui.ThemeManager.Theme; import com.cyanogenmod.filemanager.ui.dialogs.ActionsDialog; import com.cyanogenmod.filemanager.ui.dialogs.FilesystemInfoDialog; import com.cyanogenmod.filemanager.ui.dialogs.FilesystemInfoDialog.OnMountListener; import com.cyanogenmod.filemanager.ui.widgets.Breadcrumb; import com.cyanogenmod.filemanager.ui.widgets.ButtonItem; import com.cyanogenmod.filemanager.ui.widgets.NavigationCustomTitleView; import com.cyanogenmod.filemanager.ui.widgets.NavigationView; import com.cyanogenmod.filemanager.ui.widgets.NavigationView.OnNavigationRequestMenuListener; import com.cyanogenmod.filemanager.ui.widgets.NavigationView.OnNavigationSelectionChangedListener; import com.cyanogenmod.filemanager.ui.widgets.SelectionView; import com.cyanogenmod.filemanager.util.AndroidHelper; import com.cyanogenmod.filemanager.util.CommandHelper; import com.cyanogenmod.filemanager.util.DialogHelper; import com.cyanogenmod.filemanager.util.ExceptionUtil; import com.cyanogenmod.filemanager.util.FileHelper; import com.cyanogenmod.filemanager.util.StorageHelper; import java.io.File; import java.io.FileNotFoundException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * The main navigation activity. This activity is the center of the application. * From this the user can navigate, search, make actions.<br/> * This activity is singleTop, so when it is displayed no other activities exists in * the stack.<br/> * This cause an issue with the saved instance of this class, because if another activity * is displayed, and the process is killed, NavigationActivity is started and the saved * instance gets corrupted.<br/> * For this reason the methods {link {@link Activity#onSaveInstanceState(Bundle)} and * {@link Activity#onRestoreInstanceState(Bundle)} are not implemented, and every time * the app is killed, is restarted from his initial state. */ public class NavigationActivity extends Activity implements OnHistoryListener, OnRequestRefreshListener, OnNavigationRequestMenuListener, OnNavigationSelectionChangedListener { private static final String TAG = "NavigationActivity"; //$NON-NLS-1$ private static boolean DEBUG = false; /** * Intent code for request a bookmark selection. */ public static final int INTENT_REQUEST_BOOKMARK = 10001; /** * Intent code for request a history selection. */ public static final int INTENT_REQUEST_HISTORY = 20001; /** * Intent code for request a search. */ public static final int INTENT_REQUEST_SEARCH = 30001; /** * Constant for extra information about selected bookmark. */ public static final String EXTRA_BOOKMARK_SELECTION = "extra_bookmark_selection"; //$NON-NLS-1$ /** * Constant for extra information about selected history entry. */ public static final String EXTRA_HISTORY_ENTRY_SELECTION = "extra_history_entry_selection"; //$NON-NLS-1$ /** * Constant for extra information about clear selection action. */ public static final String EXTRA_HISTORY_CLEAR = "extra_history_clear_history"; //$NON-NLS-1$ /** * Constant for extra information about selected search entry. */ public static final String EXTRA_SEARCH_ENTRY_SELECTION = "extra_search_entry_selection"; //$NON-NLS-1$ /** * Constant for extra information about last search data. */ public static final String EXTRA_SEARCH_LAST_SEARCH_DATA = "extra_search_last_search_data"; //$NON-NLS-1$ /** * Constant for extra information for request a navigation to the passed path. */ public static final String EXTRA_NAVIGATE_TO = "extra_navigate_to"; //$NON-NLS-1$ // The timeout needed to reset the exit status for back button // After this time user need to tap 2 times the back button to // exit, and the toast is shown again after the first tap. private static final int RELEASE_EXIT_CHECK_TIMEOUT = 3500; private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent != null) { if (intent.getAction().compareTo(FileManagerSettings.INTENT_SETTING_CHANGED) == 0) { // The settings has changed String key = intent.getStringExtra(FileManagerSettings.EXTRA_SETTING_CHANGED_KEY); if (key != null) { // Disk usage warning level if (key.compareTo(FileManagerSettings. SETTINGS_DISK_USAGE_WARNING_LEVEL.getId()) == 0) { // Set the free disk space warning level of the breadcrumb widget Breadcrumb breadcrumb = getCurrentNavigationView().getBreadcrumb(); String fds = Preferences.getSharedPreferences().getString( FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(), (String)FileManagerSettings. SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue()); breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds)); breadcrumb.updateMountPointInfo(); return; } // Case sensitive sort if (key.compareTo(FileManagerSettings. SETTINGS_CASE_SENSITIVE_SORT.getId()) == 0) { getCurrentNavigationView().refresh(); return; } // Use flinger if (key.compareTo(FileManagerSettings. SETTINGS_USE_FLINGER.getId()) == 0) { boolean useFlinger = Preferences.getSharedPreferences().getBoolean( FileManagerSettings.SETTINGS_USE_FLINGER.getId(), ((Boolean)FileManagerSettings. SETTINGS_USE_FLINGER. getDefaultValue()).booleanValue()); getCurrentNavigationView().setUseFlinger(useFlinger); return; } // Access mode if (key.compareTo(FileManagerSettings. SETTINGS_ACCESS_MODE.getId()) == 0) { // Is it necessary to create or exit of the ChRooted? boolean chRooted = FileManagerApplication. getAccessMode().compareTo(AccessMode.SAFE) == 0; if (chRooted != NavigationActivity.this.mChRooted) { if (chRooted) { createChRooted(); } else { exitChRooted(); } } } } } else if (intent.getAction().compareTo( FileManagerSettings.INTENT_FILE_CHANGED) == 0) { // Retrieve the file that was changed String file = intent.getStringExtra(FileManagerSettings.EXTRA_FILE_CHANGED_KEY); try { FileSystemObject fso = CommandHelper.getFileInfo(context, file, null); if (fso != null) { getCurrentNavigationView().refresh(fso); } } catch (Exception e) { ExceptionUtil.translateException(context, e, true, false); } } else if (intent.getAction().compareTo( FileManagerSettings.INTENT_THEME_CHANGED) == 0) { applyTheme(); } } } }; /** * @hide */ NavigationView[] mNavigationViews; private List<History> mHistory; private int mCurrentNavigationView; private ViewGroup mActionBar; private SelectionView mSelectionBar; private boolean mExitFlag = false; private long mExitBackTimeout = -1; private View mOptionsAnchorView; private int mOrientation; /** * @hide */ boolean mChRooted; /** * @hide */ Handler mHandler; /** * {@inheritDoc} */ @Override protected void onCreate(Bundle state) { if (DEBUG) { Log.d(TAG, "NavigationActivity.onCreate"); //$NON-NLS-1$ } // Register the broadcast receiver IntentFilter filter = new IntentFilter(); filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED); filter.addAction(FileManagerSettings.INTENT_FILE_CHANGED); filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED); registerReceiver(this.mNotificationReceiver, filter); //Set the main layout of the activity setContentView(R.layout.navigation); //Initialize nfc adapter NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter != null) { mNfcAdapter.setBeamPushUrisCallback(new NfcAdapter.CreateBeamUrisCallback() { @Override public Uri[] createBeamUris(NfcEvent event) { List<FileSystemObject> selectedFiles = getCurrentNavigationView().getSelectedFiles(); if (selectedFiles.size() > 0) { List<Uri> fileUri = new ArrayList<Uri>(); for (FileSystemObject f : selectedFiles) { //Beam ignores folders and system files if (!FileHelper.isDirectory(f) && !FileHelper.isSystemFile(f)) { fileUri.add(Uri.fromFile(new File(f.getFullPath()))); } } if (fileUri.size() > 0) { return fileUri.toArray(new Uri[fileUri.size()]); } } return null; } }, this); } //Initialize activity init(); //Navigation views initNavigationViews(); //Initialize action bars initTitleActionBar(); initStatusActionBar(); initSelectionBar(); // Adjust layout (only when start on landscape mode) int orientation = getResources().getConfiguration().orientation; if (orientation == Configuration.ORIENTATION_LANDSCAPE) { onLayoutChanged(); } this.mOrientation = orientation; // Apply the theme applyTheme(); // Show welcome message showWelcomeMsg(); this.mHandler = new Handler(); this.mHandler.post(new Runnable() { @Override public void run() { //Initialize navigation int cc = NavigationActivity.this.mNavigationViews.length; for (int i = 0; i < cc; i++) { initNavigation(i, false); } //Check the intent action checkIntent(getIntent()); } }); //Save state super.onCreate(state); } /** * {@inheritDoc} */ @Override protected void onNewIntent(Intent intent) { //Initialize navigation initNavigation(this.mCurrentNavigationView, true); //Check the intent action checkIntent(intent); } /** * {@inheritDoc} */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); onLayoutChanged(); } /** * {@inheritDoc} */ @Override protected void onDestroy() { if (DEBUG) { Log.d(TAG, "NavigationActivity.onDestroy"); //$NON-NLS-1$ } // Unregister the receiver try { unregisterReceiver(this.mNotificationReceiver); } catch (Throwable ex) { /**NON BLOCK**/ } //All destroy. Continue super.onDestroy(); } /** * Method that returns the current navigation view. * * @return NavigationView The current navigation view */ public NavigationView getCurrentNavigationView() { return getNavigationView(this.mCurrentNavigationView); } /** * Method that returns the current navigation view. * * @param viewId The view to return * @return NavigationView The current navigation view */ public NavigationView getNavigationView(int viewId) { if (this.mNavigationViews == null) return null; return this.mNavigationViews[viewId]; } /** * Method that initializes the activity. */ private void init() { this.mHistory = new ArrayList<History>(); this.mChRooted = FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0; } /** * Method that displays a welcome message the first time the user * access the application */ private void showWelcomeMsg() { boolean firstUse = Preferences.getSharedPreferences().getBoolean( FileManagerSettings.SETTINGS_FIRST_USE.getId(), ((Boolean)FileManagerSettings.SETTINGS_FIRST_USE.getDefaultValue()).booleanValue()); //Display the welcome message? if (firstUse) { AlertDialog dialog = DialogHelper.createAlertDialog( this, R.drawable.ic_launcher, R.string.welcome_title, getString(R.string.welcome_msg), false); DialogHelper.delegateDialogShow(this, dialog); // Don't display again this dialog try { Preferences.savePreference( FileManagerSettings.SETTINGS_FIRST_USE, Boolean.FALSE, true); } catch (Exception e) {/**NON BLOCK**/} } } /** * Method that initializes the titlebar of the activity. */ private void initTitleActionBar() { //Inflate the view and associate breadcrumb View titleLayout = getLayoutInflater().inflate( R.layout.navigation_view_customtitle, null, false); NavigationCustomTitleView title = (NavigationCustomTitleView)titleLayout.findViewById(R.id.navigation_title_flipper); title.setOnHistoryListener(this); Breadcrumb breadcrumb = (Breadcrumb)title.findViewById(R.id.breadcrumb_view); int cc = this.mNavigationViews.length; for (int i = 0; i < cc; i++) { this.mNavigationViews[i].setBreadcrumb(breadcrumb); this.mNavigationViews[i].setOnHistoryListener(this); this.mNavigationViews[i].setOnNavigationSelectionChangedListener(this); this.mNavigationViews[i].setOnNavigationOnRequestMenuListener(this); this.mNavigationViews[i].setCustomTitle(title); } // Set the free disk space warning level of the breadcrumb widget String fds = Preferences.getSharedPreferences().getString( FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(), (String)FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue()); breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds)); //Configure the action bar options getActionBar().setBackgroundDrawable( getResources().getDrawable(R.drawable.bg_holo_titlebar)); getActionBar().setDisplayOptions( ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME); getActionBar().setCustomView(titleLayout); } /** * Method that initializes the statusbar of the activity. */ private void initStatusActionBar() { //Performs a width calculation of buttons. Buttons exceeds the width //of the action bar should be hidden //This application not use android ActionBar because the application //make uses of the title and bottom areas, and wants to force to show //the overflow button (without care of physical buttons) this.mActionBar = (ViewGroup)findViewById(R.id.navigation_actionbar); this.mActionBar.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange( View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { //Get the width of the action bar int w = v.getMeasuredWidth(); //Wake through children calculation his dimensions int bw = (int)getResources().getDimension(R.dimen.default_buttom_width); int cw = 0; final ViewGroup abView = ((ViewGroup)v); int cc = abView.getChildCount(); for (int i = 0; i < cc; i++) { View child = abView.getChildAt(i); child.setVisibility(cw + bw > w ? View.INVISIBLE : View.VISIBLE); cw += bw; } } }); // Have overflow menu? View overflow = findViewById(R.id.ab_overflow); boolean showOptionsMenu = AndroidHelper.showOptionsMenu(getApplicationContext()); overflow.setVisibility(showOptionsMenu ? View.VISIBLE : View.GONE); this.mOptionsAnchorView = showOptionsMenu ? overflow : this.mActionBar; // Show the status bar View statusBar = findViewById(R.id.navigation_statusbar_portrait_holder); statusBar.setVisibility(View.VISIBLE); } /** * Method that initializes the selectionbar of the activity. */ private void initSelectionBar() { this.mSelectionBar = (SelectionView)findViewById(R.id.navigation_selectionbar); } /** * Method that initializes the navigation views of the activity */ private void initNavigationViews() { //Get the navigation views (wishlist: multiple view; for now only one view) this.mNavigationViews = new NavigationView[1]; this.mCurrentNavigationView = 0; //- 0 this.mNavigationViews[0] = (NavigationView)findViewById(R.id.navigation_view); this.mNavigationViews[0].setId(0); } /** * Method that initializes the navigation. * * @param viewId The navigation view identifier where apply the navigation * @param restore Initialize from a restore info * @hide */ void initNavigation(final int viewId, final boolean restore) { final NavigationView navigationView = getNavigationView(viewId); this.mHandler.post(new Runnable() { @Override public void run() { //Create the default console (from the preferences) try { Console console = ConsoleBuilder.getConsole(NavigationActivity.this); if (console == null) { throw new ConsoleAllocException("console == null"); //$NON-NLS-1$ } } catch (Throwable ex) { if (!NavigationActivity.this.mChRooted) { //Show exception and exists Log.e(TAG, getString(R.string.msgs_cant_create_console), ex); // We don't have any console // Show exception and exists DialogHelper.showToast( NavigationActivity.this, R.string.msgs_cant_create_console, Toast.LENGTH_LONG); exit(); return; } // We are in a trouble (something is not allowing creating the console) // Ask the user to return to prompt or root access mode mode with a // non-privileged console, prior to make crash the application askOrExit(); return; } //Is necessary navigate? if (!restore) { //Load the preference initial directory String initialDir = Preferences.getSharedPreferences().getString( FileManagerSettings.SETTINGS_INITIAL_DIR.getId(), (String)FileManagerSettings. SETTINGS_INITIAL_DIR.getDefaultValue()); if (NavigationActivity.this.mChRooted) { // Initial directory is the first external sdcard (sdcard, emmc, usb, ...) StorageVolume[] volumes = StorageHelper.getStorageVolumes(NavigationActivity.this); if (volumes != null && volumes.length > 0) { initialDir = volumes[0].getPath(); } } //Ensure initial is an absolute directory try { initialDir = new File(initialDir).getAbsolutePath(); } catch (Throwable e) { Log.e(TAG, "Resolve of initital directory fails", e); //$NON-NLS-1$ String msg = getString( R.string.msgs_settings_invalid_initial_directory, initialDir); DialogHelper.showToast(NavigationActivity.this, msg, Toast.LENGTH_SHORT); initialDir = FileHelper.ROOT_DIRECTORY; } // Change the current directory to the preference initial directory or the // request if exists String navigateTo = getIntent().getStringExtra(EXTRA_NAVIGATE_TO); if (navigateTo != null && navigateTo.length() > 0) { navigationView.changeCurrentDir(navigateTo); } else { navigationView.changeCurrentDir(initialDir); } } } }); } /** * Method that verifies the intent passed to the activity, and checks * if a request is made like Search. * * @param intent The intent to check * @hide */ void checkIntent(Intent intent) { //Search action if (Intent.ACTION_SEARCH.equals(intent.getAction())) { Intent searchIntent = new Intent(this, SearchActivity.class); searchIntent.setAction(Intent.ACTION_SEARCH); //- SearchActivity.EXTRA_SEARCH_DIRECTORY searchIntent.putExtra( SearchActivity.EXTRA_SEARCH_DIRECTORY, getCurrentNavigationView().getCurrentDir()); //- SearchManager.APP_DATA if (intent.getBundleExtra(SearchManager.APP_DATA) != null) { Bundle bundle = new Bundle(); bundle.putAll(intent.getBundleExtra(SearchManager.APP_DATA)); searchIntent.putExtra(SearchManager.APP_DATA, bundle); } //-- SearchManager.QUERY String query = intent.getStringExtra(SearchManager.QUERY); if (query != null) { searchIntent.putExtra(SearchManager.QUERY, query); } //- android.speech.RecognizerIntent.EXTRA_RESULTS ArrayList<String> extraResults = intent.getStringArrayListExtra(android.speech.RecognizerIntent.EXTRA_RESULTS); if (extraResults != null) { searchIntent.putStringArrayListExtra( android.speech.RecognizerIntent.EXTRA_RESULTS, extraResults); } startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH); return; } // Navigate to the requested path String navigateTo = intent.getStringExtra(EXTRA_NAVIGATE_TO); if (navigateTo != null && navigateTo.length() >= 0) { getCurrentNavigationView().changeCurrentDir(navigateTo); } } /** * {@inheritDoc} */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU) { showOverflowPopUp(this.mOptionsAnchorView); return true; } if (keyCode == KeyEvent.KEYCODE_BACK) { if (checkBackAction()) { return true; } // An exit event has occurred, force the destroy the consoles exit(); } return super.onKeyUp(keyCode, event); } /** * {@inheritDoc} */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: if ((getActionBar().getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) == ActionBar.DISPLAY_HOME_AS_UP) { checkBackAction(); } return true; default: return super.onOptionsItemSelected(item); } } /** * Method invoked when an action item is clicked. * * @param view The button pushed */ public void onActionBarItemClick(View view) { switch (view.getId()) { //###################### //Navigation Custom Title //###################### case R.id.ab_configuration: //Show navigation view configuration toolbar getCurrentNavigationView().getCustomTitle().showConfigurationView(); getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); break; case R.id.ab_close: //Hide navigation view configuration toolbar getCurrentNavigationView().getCustomTitle().hideConfigurationView(); break; //###################### //Breadcrumb Actions //###################### case R.id.ab_filesystem_info: //Show information of the filesystem MountPoint mp = getCurrentNavigationView().getBreadcrumb().getMountPointInfo(); DiskUsage du = getCurrentNavigationView().getBreadcrumb().getDiskUsageInfo(); showMountPointInfo(mp, du); break; //###################### //Navigation view options //###################### case R.id.ab_sort_mode: showSettingsPopUp(view, Arrays.asList( new FileManagerSettings[]{ FileManagerSettings.SETTINGS_SORT_MODE})); break; case R.id.ab_layout_mode: showSettingsPopUp(view, Arrays.asList( new FileManagerSettings[]{ FileManagerSettings.SETTINGS_LAYOUT_MODE})); break; case R.id.ab_view_options: // If we are in ChRooted mode, then don't show non-secure items if (this.mChRooted) { showSettingsPopUp(view, Arrays.asList(new FileManagerSettings[]{ FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST})); } else { showSettingsPopUp(view, Arrays.asList(new FileManagerSettings[]{ FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST, FileManagerSettings.SETTINGS_SHOW_HIDDEN, FileManagerSettings.SETTINGS_SHOW_SYSTEM, FileManagerSettings.SETTINGS_SHOW_SYMLINKS})); } break; //###################### //Selection Actions //###################### case R.id.ab_selection_done: //Show information of the filesystem getCurrentNavigationView().onDeselectAll(); break; //###################### //Action Bar buttons //###################### case R.id.ab_actions: openActionsDialog(getCurrentNavigationView().getCurrentDir(), true); break; case R.id.ab_bookmarks: openBookmarks(); break; case R.id.ab_history: openHistory(); break; case R.id.ab_search: openSearch(); break; case R.id.ab_overflow: showOverflowPopUp(view); break; default: break; } } /** * {@inheritDoc} */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { switch (requestCode) { case INTENT_REQUEST_BOOKMARK: if (resultCode == RESULT_OK) { FileSystemObject fso = (FileSystemObject)data. getSerializableExtra(EXTRA_BOOKMARK_SELECTION); if (fso != null) { //Open the fso getCurrentNavigationView().open(fso); } } break; case INTENT_REQUEST_HISTORY: if (resultCode == RESULT_OK) { //Change current directory History history = (History)data.getSerializableExtra(EXTRA_HISTORY_ENTRY_SELECTION); navigateToHistory(history); } else if (resultCode == RESULT_CANCELED) { boolean clear = data.getBooleanExtra(EXTRA_HISTORY_CLEAR, false); if (clear) { clearHistory(); } } break; case INTENT_REQUEST_SEARCH: if (resultCode == RESULT_OK) { //Change directory? FileSystemObject fso = (FileSystemObject)data. getSerializableExtra(EXTRA_SEARCH_ENTRY_SELECTION); SearchInfoParcelable searchInfo = data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA); if (fso != null) { //Goto to new directory getCurrentNavigationView().open(fso, searchInfo); } } else if (resultCode == RESULT_CANCELED) { SearchInfoParcelable searchInfo = data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA); if (searchInfo != null && searchInfo.isSuccessNavigation()) { //Navigate to previous history back(); } else { // I don't know is the search view was changed, so try to do a refresh // of the navigation view getCurrentNavigationView().refresh(true); } } break; default: break; } } } /** * {@inheritDoc} */ @Override public void onNewHistory(HistoryNavigable navigable) { //Recollect information about current status History history = new History(this.mHistory.size(), navigable); this.mHistory.add(history); getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); } /** * {@inheritDoc} */ @Override public void onCheckHistory() { //Need to show HomeUp Button boolean enabled = this.mHistory != null && this.mHistory.size() > 0; getActionBar().setDisplayHomeAsUpEnabled(enabled); getActionBar().setHomeButtonEnabled(enabled); } /** * {@inheritDoc} */ @Override public void onRequestRefresh(Object o, boolean clearSelection) { if (o instanceof FileSystemObject) { // Refresh only the item this.getCurrentNavigationView().refresh((FileSystemObject)o); } else if (o == null) { // Refresh all getCurrentNavigationView().refresh(); } if (clearSelection) { this.getCurrentNavigationView().onDeselectAll(); } } /** * {@inheritDoc} */ @Override public void onRequestRemove(Object o, boolean clearSelection) { if (o instanceof FileSystemObject) { // Remove from view this.getCurrentNavigationView().removeItem((FileSystemObject)o); //Remove from history removeFromHistory((FileSystemObject)o); } else { onRequestRefresh(null, clearSelection); } if (clearSelection) { this.getCurrentNavigationView().onDeselectAll(); } } /** * {@inheritDoc} */ @Override public void onNavigateTo(Object o) { // Ignored } /** * {@inheritDoc} */ @Override public void onSelectionChanged(NavigationView navView, List<FileSystemObject> selectedItems) { this.mSelectionBar.setSelection(selectedItems); } /** * {@inheritDoc} */ @Override public void onRequestMenu(NavigationView navView, FileSystemObject item) { // Show the actions dialog openActionsDialog(item, false); } /** * Method that shows a popup with a menu associated a {@link FileManagerSettings}. * * @param anchor The action button that was pressed * @param settings The array of settings associated with the action button */ private void showSettingsPopUp(View anchor, List<FileManagerSettings> settings) { //Create the adapter final MenuSettingsAdapter adapter = new MenuSettingsAdapter(this, settings); //Create a show the popup menu final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor); popup.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View v, int position, long id) { FileManagerSettings setting = ((MenuSettingsAdapter)parent.getAdapter()).getSetting(position); final int value = ((MenuSettingsAdapter)parent.getAdapter()).getId(position); popup.dismiss(); try { if (setting.compareTo(FileManagerSettings.SETTINGS_LAYOUT_MODE) == 0) { //Need to change the layout getCurrentNavigationView().changeViewMode( NavigationLayoutMode.fromId(value)); } else { //Save and refresh if (setting.getDefaultValue() instanceof Enum<?>) { //Enumeration Preferences.savePreference(setting, new ObjectIdentifier() { @Override public int getId() { return value; } }, false); } else { //Boolean boolean newval = Preferences.getSharedPreferences(). getBoolean( setting.getId(), ((Boolean)setting.getDefaultValue()).booleanValue()); Preferences.savePreference(setting, Boolean.valueOf(!newval), false); } getCurrentNavigationView().refresh(); } } catch (Exception e) { Log.e(TAG, "Error applying navigation option", e); //$NON-NLS-1$ NavigationActivity.this.mHandler.post(new Runnable() { @Override public void run() { DialogHelper.showToast( NavigationActivity.this, R.string.msgs_settings_save_failure, Toast.LENGTH_SHORT); } }); } finally { adapter.dispose(); getCurrentNavigationView().getCustomTitle().restoreView(); } } }); popup.setOnDismissListener(new PopupWindow.OnDismissListener() { @Override public void onDismiss() { adapter.dispose(); } }); popup.show(); } /** * Method that shows a popup with the activity main menu. * * @param anchor The action button that was pressed */ private void showOverflowPopUp(View anchor) { SimpleMenuListAdapter adapter = new HighlightedSimpleMenuListAdapter(this, R.menu.navigation); Menu menu = adapter.getMenu(); int cc = this.mActionBar.getChildCount(); for (int i = 0, j = this.mActionBar.getChildCount() - 1; i < cc; i++, j--) { View child = this.mActionBar.getChildAt(i); boolean visible = child.getVisibility() == View.VISIBLE; if (visible) { menu.removeItem(menu.getItem(j).getItemId()); } } final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor); popup.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick( final AdapterView<?> parent, final View v, final int position, final long id) { final int itemId = (int)id; NavigationActivity.this.mHandler.post(new Runnable() { @Override public void run() { popup.dismiss(); switch (itemId) { case R.id.mnu_settings: //Settings Intent settings = new Intent( NavigationActivity.this, SettingsPreferences.class); startActivity(settings); break; case R.id.mnu_history: //History openHistory(); popup.dismiss(); break; case R.id.mnu_bookmarks: //Bookmarks openBookmarks(); popup.dismiss(); break; case R.id.mnu_search: //Search openSearch(); popup.dismiss(); break; default: break; } } }); } }); popup.show(); } /** * Method that show the information of a filesystem mount point. * * @param mp The mount point info * @param du The disk usage of the mount point */ private void showMountPointInfo(MountPoint mp, DiskUsage du) { //Has mount point info? if (mp == null) { //There is no information AlertDialog alert = DialogHelper.createWarningDialog( this, R.string.filesystem_info_warning_title, R.string.filesystem_info_warning_msg); DialogHelper.delegateDialogShow(this, alert); return; } //Show a the filesystem info dialog FilesystemInfoDialog dialog = new FilesystemInfoDialog(this, mp, du); dialog.setOnMountListener(new OnMountListener() { @Override public void onRemount(MountPoint mountPoint) { //Update the statistics of breadcrumb, only if mount point is the same Breadcrumb breadcrumb = getCurrentNavigationView().getBreadcrumb(); if (breadcrumb.getMountPointInfo().compareTo(mountPoint) == 0) { breadcrumb.updateMountPointInfo(); } } }); dialog.show(); } /** * Method that checks the action that must be realized when the * back button is pushed. * * @return boolean Indicates if the action must be intercepted */ private boolean checkBackAction() { // We need a basic structure to check this if (getCurrentNavigationView() == null) return false; //Check if the configuration view is showing. In this case back //action must be "close configuration" if (getCurrentNavigationView().getCustomTitle().isConfigurationViewShowing()) { getCurrentNavigationView().getCustomTitle().restoreView(); return true; } //Do back operation over the navigation history boolean flag = this.mExitFlag; this.mExitFlag = !back(); // Retrieve if the exit status timeout has expired long now = System.currentTimeMillis(); boolean timeout = (this.mExitBackTimeout == -1 || (now - this.mExitBackTimeout) > RELEASE_EXIT_CHECK_TIMEOUT); //Check if there no history and if the user was advised in the last back action if (this.mExitFlag && (this.mExitFlag != flag || timeout)) { //Communicate the user that the next time the application will be closed this.mExitBackTimeout = System.currentTimeMillis(); DialogHelper.showToast(this, R.string.msgs_push_again_to_exit, Toast.LENGTH_SHORT); return true; } //Back action not applied return !this.mExitFlag; } /** * {@inheritDoc} */ @Override public boolean onSearchRequested() { Bundle bundle = new Bundle(); bundle.putString( SearchActivity.EXTRA_SEARCH_DIRECTORY, getCurrentNavigationView().getCurrentDir()); startSearch(Preferences.getLastSearch(), true, bundle, false); return true; } /** * Method that returns the history size. */ private void clearHistory() { this.mHistory.clear(); onCheckHistory(); } /** * Method that navigates to the passed history reference. * * @param history The history reference * @return boolean A problem occurs while navigate */ public boolean navigateToHistory(History history) { try { //Gets the history History realHistory = this.mHistory.get(history.getPosition()); //Navigate to item. Check what kind of history is if (realHistory.getItem() instanceof NavigationViewInfoParcelable) { //Navigation NavigationViewInfoParcelable info = (NavigationViewInfoParcelable)realHistory.getItem(); int viewId = info.getId(); NavigationView view = getNavigationView(viewId); // Selected items must not be restored from on history navigation info.setSelectedFiles(view.getSelectedFiles()); view.onRestoreState(info); } else if (realHistory.getItem() instanceof SearchInfoParcelable) { //Search (open search with the search results) SearchInfoParcelable info = (SearchInfoParcelable)realHistory.getItem(); Intent searchIntent = new Intent(this, SearchActivity.class); searchIntent.setAction(SearchActivity.ACTION_RESTORE); searchIntent.putExtra(SearchActivity.EXTRA_SEARCH_RESTORE, (Parcelable)info); startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH); } else { //The type is unknown throw new IllegalArgumentException("Unknown history type"); //$NON-NLS-1$ } //Remove the old history int cc = realHistory.getPosition(); for (int i = this.mHistory.size() - 1; i >= cc; i--) { this.mHistory.remove(i); } if (this.mHistory.size() == 0) { getActionBar().setDisplayHomeAsUpEnabled(false); getActionBar().setHomeButtonEnabled(false); } //Navigate return true; } catch (Throwable ex) { if (history != null) { Log.e(TAG, String.format("Failed to navigate to history %d: %s", //$NON-NLS-1$ Integer.valueOf(history.getPosition()), history.getItem().getTitle()), ex); } else { Log.e(TAG, String.format("Failed to navigate to history: null", ex)); //$NON-NLS-1$ } this.mHandler.post(new Runnable() { @Override public void run() { DialogHelper.showToast( NavigationActivity.this, R.string.msgs_history_unknown, Toast.LENGTH_LONG); } }); //Not change directory return false; } } /** * Method that request a back action over the navigation history. * * @return boolean If a back action was applied */ public boolean back() { // Check that has valid history while (this.mHistory.size() > 0) { History h = this.mHistory.get(this.mHistory.size() - 1); if (h.getItem() instanceof NavigationViewInfoParcelable) { // Verify that the path exists String path = ((NavigationViewInfoParcelable)h.getItem()).getCurrentDir(); try { FileSystemObject info = CommandHelper.getFileInfo(this, path, null); if (info != null) { break; } this.mHistory.remove(this.mHistory.size() - 1); } catch (Exception e) { ExceptionUtil.translateException(this, e, true, false); this.mHistory.remove(this.mHistory.size() - 1); } } else { break; } } //Extract a history from the if (this.mHistory.size() > 0) { //Navigate to history return navigateToHistory(this.mHistory.get(this.mHistory.size() - 1)); } //Nothing to apply return false; } /** * Method that opens the actions dialog * * @param item The path or the {@link FileSystemObject} * @param global If the menu to display is the one with global actions */ private void openActionsDialog(Object item, boolean global) { // Resolve the full path String path = String.valueOf(item); if (item instanceof FileSystemObject) { path = ((FileSystemObject)item).getFullPath(); } // Prior to show the dialog, refresh the item reference FileSystemObject fso = null; try { fso = CommandHelper.getFileInfo(this, path, false, null); if (fso == null) { throw new NoSuchFileOrDirectory(path); } } catch (Exception e) { // Notify the user ExceptionUtil.translateException(this, e); // Remove the object if (e instanceof FileNotFoundException || e instanceof NoSuchFileOrDirectory) { // If have a FileSystemObject reference then there is no need to search // the path (less resources used) if (item instanceof FileSystemObject) { getCurrentNavigationView().removeItem((FileSystemObject)item); } else { getCurrentNavigationView().removeItem((String)item); } } return; } // Show the dialog ActionsDialog dialog = new ActionsDialog(this, fso, global, false); dialog.setOnRequestRefreshListener(this); dialog.setOnSelectionListener(getCurrentNavigationView()); dialog.show(); } /** * Method that opens the bookmarks activity. * @hide */ void openBookmarks() { Intent bookmarksIntent = new Intent(this, BookmarksActivity.class); bookmarksIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME); startActivityForResult(bookmarksIntent, INTENT_REQUEST_BOOKMARK); } /** * Method that opens the history activity. * @hide */ void openHistory() { Intent historyIntent = new Intent(this, HistoryActivity.class); historyIntent.putExtra(HistoryActivity.EXTRA_HISTORY_LIST, (Serializable)this.mHistory); historyIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME); startActivityForResult(historyIntent, INTENT_REQUEST_HISTORY); } /** * Method that opens the search activity. * @hide */ void openSearch() { onSearchRequested(); } /** * Method that remove the {@link FileSystemObject} from the history */ private void removeFromHistory(FileSystemObject fso) { if (this.mHistory != null) { int cc = this.mHistory.size(); for (int i = cc-1; i >= 0 ; i--) { History history = this.mHistory.get(i); if (history.getItem() instanceof NavigationViewInfoParcelable) { String p0 = fso.getFullPath(); String p1 = ((NavigationViewInfoParcelable)history.getItem()).getCurrentDir(); if (p0.compareTo(p1) == 0) { this.mHistory.remove(i); } } } } } /** * Method that ask the user to change the access mode prior to crash. * @hide */ void askOrExit() { //Show a dialog asking the user AlertDialog dialog = DialogHelper.createYesNoDialog( this, R.string.msgs_change_to_prompt_access_mode_title, R.string.msgs_change_to_prompt_access_mode_msg, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface alertDialog, int which) { if (which == DialogInterface.BUTTON_NEGATIVE) { // We don't have any console // Show exception and exists DialogHelper.showToast( NavigationActivity.this, R.string.msgs_cant_create_console, Toast.LENGTH_LONG); exit(); return; } // Ok. Now try to change to prompt mode. Any crash // here is a fatal error. We won't have any console to operate. try { // Change console ConsoleBuilder.changeToNonPrivilegedConsole(NavigationActivity.this); // Save preferences Preferences.savePreference( FileManagerSettings.SETTINGS_ACCESS_MODE, AccessMode.PROMPT, true); } catch (Exception e) { // Displays an exception and exit Log.e(TAG, getString(R.string.msgs_cant_create_console), e); DialogHelper.showToast( NavigationActivity.this, R.string.msgs_cant_create_console, Toast.LENGTH_LONG); exit(); } } }); DialogHelper.delegateDialogShow(this, dialog); } /** * Method that creates a ChRooted environment, protecting the user to break anything in * the device * @hide */ void createChRooted() { // If we are in a ChRooted mode, then do nothing if (this.mChRooted) return; this.mChRooted = true; int cc = this.mNavigationViews.length; for (int i = 0; i < cc; i++) { this.mNavigationViews[i].createChRooted(); } // Remove the selection cc = this.mNavigationViews.length; for (int i = 0; i < cc; i++) { getCurrentNavigationView().onDeselectAll(); } // Remove the history (don't allow to access to previous data) clearHistory(); } /** * Method that exits from a ChRooted * @hide */ void exitChRooted() { // If we aren't in a ChRooted mode, then do nothing if (!this.mChRooted) return; this.mChRooted = false; int cc = this.mNavigationViews.length; for (int i = 0; i < cc; i++) { this.mNavigationViews[i].exitChRooted(); } } /** * Method called when a controlled exit is required * @hide */ void exit() { try { FileManagerApplication.destroyBackgroundConsole(); } catch (Throwable ex) { /**NON BLOCK**/ } try { ConsoleBuilder.destroyConsole(); } catch (Throwable ex) { /**NON BLOCK**/ } finish(); } /** * Method that reconfigures the layout for better fit in portrait and landscape modes */ private void onLayoutChanged() { Theme theme = ThemeManager.getCurrentTheme(this); // Apply only when the orientation was changed int orientation = getResources().getConfiguration().orientation; if (this.mOrientation == orientation) return; this.mOrientation = orientation; if (this.mOrientation == Configuration.ORIENTATION_LANDSCAPE) { // Landscape mode ViewGroup statusBar = (ViewGroup)findViewById(R.id.navigation_statusbar); if (statusBar.getParent() != null) { ViewGroup parent = (ViewGroup) statusBar.getParent(); parent.removeView(statusBar); } // Calculate the action button size (all the buttons must fit in the title bar) int bw = (int)getResources().getDimension(R.dimen.default_buttom_width); int abw = this.mActionBar.getChildCount() * bw; int rbw = 0; int cc = statusBar.getChildCount(); for (int i = 0; i < cc; i++) { View child = statusBar.getChildAt(i); if (child instanceof ButtonItem) { rbw += bw; } } int w = abw + rbw; boolean showOptionsMenu = AndroidHelper.showOptionsMenu(getApplicationContext()); if (!showOptionsMenu) { w -= bw; } // Add to the new location ViewGroup newParent = (ViewGroup)findViewById(R.id.navigation_title_landscape_holder); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( w, ViewGroup.LayoutParams.MATCH_PARENT); statusBar.setLayoutParams(params); newParent.addView(statusBar); // Apply theme theme.setBackgroundDrawable(this, statusBar, "titlebar_drawable"); //$NON-NLS-1$ // Hide holder View holder = findViewById(R.id.navigation_statusbar_portrait_holder); holder.setVisibility(View.GONE); } else { // Portrait mode ViewGroup statusBar = (ViewGroup)findViewById(R.id.navigation_statusbar); if (statusBar.getParent() != null) { ViewGroup parent = (ViewGroup) statusBar.getParent(); parent.removeView(statusBar); } // Add to the new location ViewGroup newParent = (ViewGroup)findViewById( R.id.navigation_statusbar_portrait_holder); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); statusBar.setLayoutParams(params); newParent.addView(statusBar); // Apply theme theme.setBackgroundDrawable(this, statusBar, "statusbar_drawable"); //$NON-NLS-1$ // Show holder newParent.setVisibility(View.VISIBLE); } } /** * Method that applies the current theme to the activity * @hide */ void applyTheme() { int orientation = getResources().getConfiguration().orientation; Theme theme = ThemeManager.getCurrentTheme(this); theme.setBaseTheme(this, false); //- Layout View v = findViewById(R.id.navigation_layout); theme.setBackgroundDrawable(this, v, "background_drawable"); //$NON-NLS-1$ //- ActionBar theme.setTitlebarDrawable(this, getActionBar(), "titlebar_drawable"); //$NON-NLS-1$ //- StatusBar v = findViewById(R.id.navigation_statusbar); if (orientation == Configuration.ORIENTATION_LANDSCAPE) { theme.setBackgroundDrawable(this, v, "titlebar_drawable"); //$NON-NLS-1$ } else { theme.setBackgroundDrawable(this, v, "statusbar_drawable"); //$NON-NLS-1$ } v = findViewById(R.id.ab_overflow); theme.setImageDrawable(this, (ImageView)v, "ab_overflow_drawable"); //$NON-NLS-1$ v = findViewById(R.id.ab_actions); theme.setImageDrawable(this, (ImageView)v, "ab_actions_drawable"); //$NON-NLS-1$ v = findViewById(R.id.ab_search); theme.setImageDrawable(this, (ImageView)v, "ab_search_drawable"); //$NON-NLS-1$ v = findViewById(R.id.ab_bookmarks); theme.setImageDrawable(this, (ImageView)v, "ab_bookmarks_drawable"); //$NON-NLS-1$ v = findViewById(R.id.ab_history); theme.setImageDrawable(this, (ImageView)v, "ab_history_drawable"); //$NON-NLS-1$ //- Expanders v = findViewById(R.id.ab_configuration); theme.setImageDrawable(this, (ImageView)v, "expander_open_drawable"); //$NON-NLS-1$ v = findViewById(R.id.ab_close); theme.setImageDrawable(this, (ImageView)v, "expander_close_drawable"); //$NON-NLS-1$ v = findViewById(R.id.ab_sort_mode); theme.setImageDrawable(this, (ImageView)v, "ab_sort_mode_drawable"); //$NON-NLS-1$ v = findViewById(R.id.ab_layout_mode); theme.setImageDrawable(this, (ImageView)v, "ab_layout_mode_drawable"); //$NON-NLS-1$ v = findViewById(R.id.ab_view_options); theme.setImageDrawable(this, (ImageView)v, "ab_view_options_drawable"); //$NON-NLS-1$ //- SelectionBar v = findViewById(R.id.navigation_selectionbar); theme.setBackgroundDrawable(this, v, "selectionbar_drawable"); //$NON-NLS-1$ v = findViewById(R.id.ab_selection_done); theme.setImageDrawable(this, (ImageView)v, "ab_selection_done_drawable"); //$NON-NLS-1$ v = findViewById(R.id.navigation_status_selection_label); theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$ //- NavigationView int cc = this.mNavigationViews.length; for (int i = 0; i < cc; i++) { getNavigationView(i).applyTheme(); } } }