/* * 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.os.Bundle; import android.os.Parcelable; import android.preference.PreferenceActivity; import android.provider.SearchRecentSuggestions; import android.text.Html; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.ProgressBar; 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.activities.preferences.SettingsPreferences.SearchPreferenceFragment; import com.cyanogenmod.filemanager.adapters.SearchResultAdapter; import com.cyanogenmod.filemanager.commands.AsyncResultExecutable; import com.cyanogenmod.filemanager.commands.AsyncResultListener; import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory; import com.cyanogenmod.filemanager.console.RelaunchableException; import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener; import com.cyanogenmod.filemanager.model.Directory; import com.cyanogenmod.filemanager.model.FileSystemObject; import com.cyanogenmod.filemanager.model.ParentDirectory; import com.cyanogenmod.filemanager.model.Query; import com.cyanogenmod.filemanager.model.SearchResult; import com.cyanogenmod.filemanager.model.Symlink; import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable; import com.cyanogenmod.filemanager.preferences.AccessMode; import com.cyanogenmod.filemanager.preferences.FileManagerSettings; import com.cyanogenmod.filemanager.preferences.Preferences; import com.cyanogenmod.filemanager.providers.RecentSearchesContentProvider; import com.cyanogenmod.filemanager.tasks.SearchResultDrawingAsyncTask; 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.MessageProgressDialog; import com.cyanogenmod.filemanager.ui.policy.DeleteActionPolicy; import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy; import com.cyanogenmod.filemanager.ui.widgets.ButtonItem; import com.cyanogenmod.filemanager.ui.widgets.FlingerListView; import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerListener; import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder; import com.cyanogenmod.filemanager.util.CommandHelper; import com.cyanogenmod.filemanager.util.DialogHelper; import com.cyanogenmod.filemanager.util.ExceptionUtil; import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult; import com.cyanogenmod.filemanager.util.FileHelper; import com.cyanogenmod.filemanager.util.StorageHelper; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; /** * An activity for search files and folders. */ public class SearchActivity extends Activity implements AsyncResultListener, OnItemClickListener, OnItemLongClickListener, OnRequestRefreshListener { private static final String TAG = "SearchActivity"; //$NON-NLS-1$ private static boolean DEBUG = false; /** * An {@link Intent} action for restore view information. */ public static final String ACTION_RESTORE = "com.cyanogenmod.filemanager.activities.SearchActivity#Restore"; //$NON-NLS-1$ /** * Intent extra parameter for search in the selected directory on enter. */ public static final String EXTRA_SEARCH_DIRECTORY = "extra_search_directory"; //$NON-NLS-1$ /** * Intent extra parameter for pass the restore information. */ public static final String EXTRA_SEARCH_RESTORE = "extra_search_restore"; //$NON-NLS-1$ //Minimum characters to allow query private static final int MIN_CHARS_SEARCH = 3; 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) { if (SearchActivity.this.mSearchListView.getAdapter() != null && (key.compareTo( FileManagerSettings. SETTINGS_HIGHLIGHT_TERMS.getId()) == 0 || key.compareTo( FileManagerSettings. SETTINGS_SHOW_RELEVANCE_WIDGET.getId()) == 0 || key.compareTo( FileManagerSettings. SETTINGS_SORT_SEARCH_RESULTS_MODE.getId()) == 0)) { // Recreate the adapter int pos = SearchActivity. this.mSearchListView.getFirstVisiblePosition(); drawResults(); SearchActivity.this.mSearchListView.setSelection(pos); return; } } } else if (intent.getAction().compareTo( FileManagerSettings.INTENT_THEME_CHANGED) == 0) { applyTheme(); } } } }; /** * A listener for flinging events from {@link FlingerListView} */ private final OnItemFlingerListener mOnItemFlingerListener = new OnItemFlingerListener() { @Override public boolean onItemFlingerStart( AdapterView<?> parent, View view, int position, long id) { try { // Response if the item can be removed SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter(); SearchResult result = adapter.getItem(position); if (result != null && result.getFso() != null) { if (result.getFso() instanceof ParentDirectory) { // This is not possible ... return false; } return true; } } catch (Exception e) { ExceptionUtil.translateException(SearchActivity.this, e, true, false); } return false; } @Override public void onItemFlingerEnd(OnItemFlingerResponder responder, AdapterView<?> parent, View view, int position, long id) { try { // Response if the item can be removed SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter(); SearchResult result = adapter.getItem(position); if (result != null && result.getFso() != null) { DeleteActionPolicy.removeFileSystemObject( SearchActivity.this, result.getFso(), null, SearchActivity.this, responder); return; } // Cancels the flinger operation responder.cancel(); } catch (Exception e) { ExceptionUtil.translateException(SearchActivity.this, e, true, false); responder.cancel(); } } }; /** * @hide */ MessageProgressDialog mDialog = null; /** * @hide */ AsyncResultExecutable mExecutable = null; /** * @hide */ ListView mSearchListView; /** * @hide */ ProgressBar mSearchWaiting; /** * @hide */ TextView mSearchFoundItems; /** * @hide */ TextView mSearchTerms; private View mEmptyListMsg; private String mSearchDirectory; /** * @hide */ List<FileSystemObject> mResultList; /** * @hide */ Query mQuery; /** * @hide */ SearchInfoParcelable mRestoreState; private SearchResultDrawingAsyncTask mDrawingSearchResultTask; /** * @hide */ boolean mChRooted; /** * {@inheritDoc} */ @Override protected void onCreate(Bundle state) { if (DEBUG) { Log.d(TAG, "SearchActivity.onCreate"); //$NON-NLS-1$ } // Check if app is running in chrooted mode this.mChRooted = FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0; // Register the broadcast receiver IntentFilter filter = new IntentFilter(); filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED); filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED); registerReceiver(this.mNotificationReceiver, filter); //Set in transition overridePendingTransition(R.anim.translate_to_right_in, R.anim.hold_out); //Set the main layout of the activity setContentView(R.layout.search); //Restore state if (state != null) { restoreState(state); } //Initialize action bars and search initTitleActionBar(); initComponents(); // Apply current theme applyTheme(); if (this.mRestoreState != null) { //Restore activity from cached data loadFromCacheData(); } else { //New query if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) { initSearch(); } else if (ACTION_RESTORE.equals(getIntent().getAction())) { restoreState(getIntent().getExtras()); loadFromCacheData(); } } //Save state super.onCreate(state); } /** * {@inheritDoc} */ @Override protected void onDestroy() { if (DEBUG) { Log.d(TAG, "SearchActivity.onDestroy"); //$NON-NLS-1$ } // Unregister the receiver try { unregisterReceiver(this.mNotificationReceiver); } catch (Throwable ex) { /**NON BLOCK**/ } //All destroy. Continue super.onDestroy(); } /** * {@inheritDoc} */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } /** * {@inheritDoc} */ @Override protected void onNewIntent(Intent intent) { //New query if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) { initSearch(); } } /** * {@inheritDoc} */ @Override protected void onPause() { //Set out transition overridePendingTransition(R.anim.hold_in, R.anim.translate_to_left_out); super.onPause(); } /** * {@inheritDoc} */ @Override protected void onSaveInstanceState(Bundle outState) { if (DEBUG) { Log.d(TAG, "SearchActivity.onSaveInstanceState"); //$NON-NLS-1$ } saveState(outState); super.onSaveInstanceState(outState); } /** * Method that save the instance of the activity. * * @param state The current state of the activity */ private void saveState(Bundle state) { try { if (this.mSearchListView.getAdapter() != null) { state.putParcelable(EXTRA_SEARCH_RESTORE, createSearchInfo()); } } catch (Throwable ex) { Log.w(TAG, "The state can't be saved", ex); //$NON-NLS-1$ } } /** * Method that restore the instance of the activity. * * @param state The previous state of the activity */ private void restoreState(Bundle state) { try { if (state.containsKey(EXTRA_SEARCH_RESTORE)) { this.mRestoreState = state.getParcelable(EXTRA_SEARCH_RESTORE); } } catch (Throwable ex) { Log.w(TAG, "The state can't be restored", ex); //$NON-NLS-1$ } } /** * Method that initializes the titlebar of the activity. */ private void initTitleActionBar() { //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().setDisplayHomeAsUpEnabled(true); View customTitle = getLayoutInflater().inflate(R.layout.simple_customtitle, null, false); TextView title = (TextView)customTitle.findViewById(R.id.customtitle_title); title.setText(R.string.search); title.setContentDescription(getString(R.string.search)); ButtonItem configuration = (ButtonItem)customTitle.findViewById(R.id.ab_button1); configuration.setImageResource(R.drawable.ic_holo_light_config); configuration.setVisibility(View.VISIBLE); getActionBar().setCustomView(customTitle); } /** * Method that initializes the component of the activity. */ private void initComponents() { //Empty list view this.mEmptyListMsg = findViewById(R.id.search_empty_msg); //The list view this.mSearchListView = (ListView)findViewById(R.id.search_listview); this.mSearchListView.setOnItemClickListener(this); this.mSearchListView.setOnItemLongClickListener(this); // If we should set the listview to response to flinger gesture detection boolean useFlinger = Preferences.getSharedPreferences().getBoolean( FileManagerSettings.SETTINGS_USE_FLINGER.getId(), ((Boolean)FileManagerSettings. SETTINGS_USE_FLINGER. getDefaultValue()).booleanValue()); if (useFlinger) { ((FlingerListView)this.mSearchListView). setOnItemFlingerListener(this.mOnItemFlingerListener); } //Other components this.mSearchWaiting = (ProgressBar)findViewById(R.id.search_waiting); this.mSearchFoundItems = (TextView)findViewById(R.id.search_status_found_items); setFoundItems(0, ""); //$NON-NLS-1$ this.mSearchTerms = (TextView)findViewById(R.id.search_status_query_terms); this.mSearchTerms.setText( Html.fromHtml(getString(R.string.search_terms, ""))); //$NON-NLS-1$ } /** * Method invoked when an action item is clicked. * * @param view The button pushed */ public void onActionBarItemClick(View view) { switch (view.getId()) { case R.id.ab_button1: //Settings Intent settings = new Intent(this, SettingsPreferences.class); settings.putExtra( PreferenceActivity.EXTRA_SHOW_FRAGMENT, SearchPreferenceFragment.class.getName()); startActivity(settings); break; default: break; } } /** * Method that initializes the titlebar of the activity. */ private void initSearch() { //Stop any pending action try { if (SearchActivity.this.mDrawingSearchResultTask != null && SearchActivity.this.mDrawingSearchResultTask.isRunning()) { SearchActivity.this.mDrawingSearchResultTask.cancel(true); } } catch (Throwable ex2) { /**NON BLOCK**/ } try { if (SearchActivity.this.mDialog != null) { SearchActivity.this.mDialog.dismiss(); } } catch (Throwable ex2) { /**NON BLOCK**/ } //Recovery the search directory Bundle bundle = getIntent().getBundleExtra(SearchManager.APP_DATA); //If data is not present, use root directory to do the search this.mSearchDirectory = FileHelper.ROOT_DIRECTORY; if (bundle != null) { this.mSearchDirectory = bundle.getString(EXTRA_SEARCH_DIRECTORY, FileHelper.ROOT_DIRECTORY); } //Retrieve the query ¿from voice recognizer? boolean voiceQuery = true; List<String> userQueries = getIntent().getStringArrayListExtra(android.speech.RecognizerIntent.EXTRA_RESULTS); if (userQueries == null || userQueries.size() == 0) { //From input text userQueries = new ArrayList<String>(); //Recovers and save the last term search in the memory Preferences.setLastSearch(getIntent().getStringExtra(SearchManager.QUERY)); userQueries.add(Preferences.getLastSearch()); voiceQuery = false; } //Filter the queries? Needed if queries come from voice recognition final List<String> filteredUserQueries = (voiceQuery) ? filterQuery(userQueries) : userQueries; //Create the queries this.mQuery = new Query().fillSlots(filteredUserQueries); List<String> queries = this.mQuery.getQueries(); //Check if some queries has lower than allowed, in this case //request the user for stop the search boolean ask = false; int cc = queries.size(); for (int i = 0; i < cc; i++) { if (queries.get(i).trim().length() < MIN_CHARS_SEARCH) { ask = true; break; } } if (ask) { askUserBeforeSearch(voiceQuery, this.mQuery, this.mSearchDirectory); } else { doSearch(voiceQuery, this.mQuery, this.mSearchDirectory); } } /** * Method that ask the user before do the search. * * @param voiceQuery Indicates if the query is from voice recognition * @param query The terms of the search * @param searchDirectory The directory of the search */ private void askUserBeforeSearch( final boolean voiceQuery, final Query query, final String searchDirectory) { //Show a dialog asking the user AlertDialog dialog = DialogHelper.createYesNoDialog( this, R.string.search_few_characters_title, R.string.search_few_characters_msg, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface alertDialog, int which) { if (which == DialogInterface.BUTTON_POSITIVE) { doSearch(voiceQuery, query, searchDirectory); return; } //Close search activity back(true, null, false); } }); DialogHelper.delegateDialogShow(this, dialog); } /** * Method that do the search. * * @param voiceQuery Indicates if the query is from voice recognition * @param query The terms of the search * @param searchDirectory The directory of the search * @hide */ void doSearch( final boolean voiceQuery, final Query query, final String searchDirectory) { // Recovers the user preferences about save suggestions boolean saveSuggestions = Preferences.getSharedPreferences().getBoolean( FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS.getId(), ((Boolean)FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS. getDefaultValue()).booleanValue()); if (saveSuggestions) { //Save every query for use as recent suggestions SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, RecentSearchesContentProvider.AUTHORITY, RecentSearchesContentProvider.MODE); if (!voiceQuery) { List<String> queries = query.getQueries(); int cc = queries.size(); for (int i = 0; i < cc; i++) { suggestions.saveRecentQuery(queries.get(i), null); } } } //Set the listview this.mResultList = new ArrayList<FileSystemObject>(); SearchResultAdapter adapter = new SearchResultAdapter(this, new ArrayList<SearchResult>(), R.layout.search_item, this.mQuery); this.mSearchListView.setAdapter(adapter); //Set terms this.mSearchTerms.setText( Html.fromHtml(getString(R.string.search_terms, query.getTerms()))); //Now, do the search in background this.mSearchListView.post(new Runnable() { @Override public void run() { try { //Retrieve the terms of the search String label = getString(R.string.searching_action_label); //Show a dialog for the progress SearchActivity.this.mDialog = new MessageProgressDialog( SearchActivity.this, 0, R.string.searching, label, true); // Initialize the setProgressMsg(0); // Set the cancel listener SearchActivity.this.mDialog.setOnCancelListener( new MessageProgressDialog.OnCancelListener() { @Override public boolean onCancel() { //User has requested the cancellation of the search //Broadcast the cancellation if (!SearchActivity.this.mExecutable.isCancelled()) { if (SearchActivity.this.mExecutable.cancel()) { ListAdapter listAdapter = SearchActivity. this.mSearchListView.getAdapter(); if (listAdapter != null) { SearchActivity.this.toggleResults( listAdapter.getCount() > 0, true); } return true; } return false; } return true; } }); SearchActivity.this.mDialog.show(); //Execute the query (search are process in background) SearchActivity.this.mExecutable = CommandHelper.findFiles( SearchActivity.this, searchDirectory, SearchActivity.this.mQuery, SearchActivity.this, null); } catch (Throwable ex) { //Remove all elements try { SearchActivity.this.removeAll(); } catch (Throwable ex2) { /**NON BLOCK**/ } try { if (SearchActivity.this.mDialog != null) { SearchActivity.this.mDialog.dismiss(); } } catch (Throwable ex2) { /**NON BLOCK**/ } //Capture the exception Log.e(TAG, "Search failed", ex); //$NON-NLS-1$ DialogHelper.showToast( SearchActivity.this, R.string.search_error_msg, Toast.LENGTH_SHORT); SearchActivity.this.mSearchListView.setVisibility(View.GONE); } } }); } /** * Method that restore the activity from the cached data. */ private void loadFromCacheData() { this.mSearchListView.post(new Runnable() { @Override public void run() { //Toggle results List<SearchResult> list = SearchActivity.this.mRestoreState.getSearchResultList(); String directory = SearchActivity.this.mRestoreState.getSearchDirectory(); SearchActivity.this.toggleResults(list.size() > 0, true); setFoundItems(list.size(), directory); //Set terms Query query = SearchActivity.this.mRestoreState.getSearchQuery(); String terms = TextUtils.join(" | ", //$NON-NLS-1$; query.getQueries().toArray(new String[]{})); if (terms.endsWith(" | ")) { //$NON-NLS-1$; terms = ""; //$NON-NLS-1$; } SearchActivity.this.mSearchTerms.setText( Html.fromHtml(getString(R.string.search_terms, terms))); try { if (SearchActivity.this.mSearchWaiting != null) { SearchActivity.this.mSearchWaiting.setVisibility(View.VISIBLE); } //Add list to the listview if (SearchActivity.this.mSearchListView.getAdapter() != null) { ((SearchResultAdapter)SearchActivity.this. mSearchListView.getAdapter()).clear(); } SearchResultAdapter adapter = new SearchResultAdapter( SearchActivity.this.mSearchListView.getContext(), list, R.layout.search_item, query); SearchActivity.this.mSearchListView.setAdapter(adapter); SearchActivity.this.mSearchListView.setSelection(0); } catch (Throwable ex) { //Capture the exception ExceptionUtil.translateException(SearchActivity.this, ex); } finally { //Hide waiting if (SearchActivity.this.mSearchWaiting != null) { SearchActivity.this.mSearchWaiting.setVisibility(View.GONE); } } } }); } /** * Method that filter the user queries for valid queries only.<br/> * <br/> * Only allow query strings with more that 3 characters * * @param original The original user queries * @return List<String> The list of queries filtered */ @SuppressWarnings("static-method") private List<String> filterQuery(List<String> original) { List<String> dst = new ArrayList<String>(original); int cc = dst.size(); for (int i = cc - 1; i >= 0; i--) { String query = dst.get(i); if (query == null || query.trim().length() < MIN_CHARS_SEARCH) { dst.remove(i); } } return dst; } /** * Method that removes all items and display a message. * @hide */ void removeAll() { SearchResultAdapter adapter = (SearchResultAdapter)this.mSearchListView.getAdapter(); adapter.clear(); adapter.notifyDataSetChanged(); this.mSearchListView.setSelection(0); toggleResults(false, true); } /** * Method that toggle the views when there are results. * * @param hasResults Indicates if there are results * @param showEmpty Show the empty list message * @hide */ void toggleResults(boolean hasResults, boolean showEmpty) { this.mSearchListView.setVisibility(hasResults ? View.VISIBLE : View.INVISIBLE); this.mEmptyListMsg.setVisibility(!hasResults && showEmpty ? View.VISIBLE : View.INVISIBLE); } /** * Method that display the number of found items. * * @param items The number of items * @param searchDirectory The search directory path * @hide */ void setFoundItems(final int items, final String searchDirectory) { if (this.mSearchFoundItems != null) { this.mSearchFoundItems.post(new Runnable() { @Override public void run() { String directory = searchDirectory; if (SearchActivity.this.mChRooted && directory != null && directory.length() > 0) { directory = StorageHelper.getChrootedPath(directory); } String foundItems = getResources(). getQuantityString( R.plurals.search_found_items, items, Integer.valueOf(items)); SearchActivity.this.mSearchFoundItems.setText( getString( R.string.search_found_items_in_directory, foundItems, directory)); } }); } } /** * {@inheritDoc} */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: back(true, null, false); return true; default: return super.onKeyUp(keyCode, event); } } /** * {@inheritDoc} */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: back(true, null, false); return true; default: return super.onOptionsItemSelected(item); } } /** * {@inheritDoc} */ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { try { SearchResult result = ((SearchResultAdapter)parent.getAdapter()).getItem(position); FileSystemObject fso = result.getFso(); if (fso instanceof Directory) { back(false, fso, false); return; } else if (fso instanceof Symlink) { Symlink symlink = (Symlink)fso; if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) { back(false, symlink.getLinkRef(), false); return; } fso = symlink.getLinkRef(); } // Open the file with the preferred registered app back(false, fso, false); } catch (Throwable ex) { ExceptionUtil.translateException(this.mSearchListView.getContext(), ex); } } /** * {@inheritDoc} */ @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { // Different actions depending on user preference // Get the adapter, the search result and the fso SearchResultAdapter adapter = ((SearchResultAdapter)parent.getAdapter()); SearchResult searchResult = adapter.getItem(position); FileSystemObject fso = searchResult.getFso(); // Open the actions menu onRequestMenu(fso); return true; //Always consume the event } /** * Method invoked when a request to show the menu associated * with an item is started. * * @param item The item for which the request was started */ public void onRequestMenu(FileSystemObject item) { // Prior to show the dialog, refresh the item reference FileSystemObject fso = null; try { fso = CommandHelper.getFileInfo(this, item.getFullPath(), false, null); if (fso == null) { throw new NoSuchFileOrDirectory(item.getFullPath()); } } catch (Exception e) { // Notify the user ExceptionUtil.translateException(this, e); // Remove the object if (e instanceof FileNotFoundException || e instanceof NoSuchFileOrDirectory) { removeItem(item); } return; } ActionsDialog dialog = new ActionsDialog(this, fso, false, true); dialog.setOnRequestRefreshListener(this); dialog.show(); } /** * Method that removes the {@link FileSystemObject} reference * * @param fso The file system object */ private void removeItem(FileSystemObject fso) { SearchResultAdapter adapter = (SearchResultAdapter)this.mSearchListView.getAdapter(); if (adapter != null) { int pos = adapter.getPosition(fso); if (pos != -1) { SearchResult sr = adapter.getItem(pos); adapter.remove(sr); } // Toggle resultset? toggleResults(adapter.getCount() > 0, true); setFoundItems(adapter.getCount(), this.mSearchDirectory); } } /** * {@inheritDoc} */ @Override public void onRequestRefresh(Object o, boolean clearSelection) { // Refresh only the item SearchResultAdapter adapter = (SearchResultAdapter)this.mSearchListView.getAdapter(); if (adapter != null) { if (o instanceof FileSystemObject) { FileSystemObject fso = (FileSystemObject)o; int pos = adapter.getPosition(fso); if (pos >= 0) { SearchResult sr = adapter.getItem(pos); sr.setFso(fso); } } else if (o == null) { // Refresh all List<SearchResult> results = adapter.getData(); this.mResultList = new ArrayList<FileSystemObject>(results.size()); int cc = results.size(); for (int i = 0; i < cc; i++) { this.mResultList.add(results.get(i).getFso()); } drawResults(); } } } /** * {@inheritDoc} */ @Override public void onRequestRemove(Object o, boolean clearSelection) { if (o instanceof FileSystemObject) { removeItem((FileSystemObject)o); } } /** * {@inheritDoc} */ @Override public void onNavigateTo(Object o) { if (o instanceof FileSystemObject) { back(false, (FileSystemObject)o, true); } } /** * Method that returns to previous activity. * * @param cancelled Indicates if the activity was cancelled * @param item The fso * @param isChecked If the fso was fully retrieve previously to this call. Otherwise, a * getFileInfo call is done to complete the fso information * @hide */ void back(final boolean cancelled, FileSystemObject item, boolean isChecked) { final Context ctx = SearchActivity.this; final Intent intent = new Intent(); boolean finish = true; if (cancelled) { if (SearchActivity.this.mDrawingSearchResultTask != null && SearchActivity.this.mDrawingSearchResultTask.isRunning()) { SearchActivity.this.mDrawingSearchResultTask.cancel(true); } if (this.mRestoreState != null) { intent.putExtra( NavigationActivity.EXTRA_SEARCH_LAST_SEARCH_DATA, (Parcelable)this.mRestoreState); } setResult(RESULT_CANCELED, intent); } else { // Check that the bookmark exists FileSystemObject fso = item; try { if (!isChecked) { fso = CommandHelper.getFileInfo(ctx, item.getFullPath(), null); } finish = navigateTo(fso, intent); } catch (Exception e) { // Capture the exception final FileSystemObject fFso = fso; final OnRelaunchCommandResult relaunchListener = new OnRelaunchCommandResult() { @Override public void onSuccess() { if (navigateTo(fFso, intent)) { finish(); } } @Override public void onFailed(Throwable cause) { ExceptionUtil.translateException(ctx, cause, false, false); } @Override public void onCancelled() { /** NON BLOCK**/} }; ExceptionUtil.translateException(ctx, e, false, true, relaunchListener); if (!(e instanceof RelaunchableException)) { if (e instanceof NoSuchFileOrDirectory || e instanceof FileNotFoundException) { // The fso not exists, delete the fso from the search try { removeItem(fso); } catch (Exception ex) {/**NON BLOCK**/} } } return; } } // End this activity if (finish) { finish(); } } /** * Method that navigate to the file system used the intent (NavigationActivity) * * @param fso The file system object to navigate to * @param intent The intent used to navigate to * @return boolean If the action implies finish this activity */ boolean navigateTo(FileSystemObject fso, Intent intent) { if (fso != null) { if (FileHelper.isDirectory(fso)) { intent.putExtra(NavigationActivity.EXTRA_SEARCH_ENTRY_SELECTION, fso); intent.putExtra( NavigationActivity.EXTRA_SEARCH_LAST_SEARCH_DATA, (Parcelable)createSearchInfo()); setResult(RESULT_OK, intent); return true; } // Open the file here, so when focus back to the app, the search activity // its in top of the stack IntentsActionPolicy.openFileSystemObject(this, fso, false, null, null); } else { // The fso not exists, delete the fso from the search try { removeItem(fso); } catch (Exception ex) {/**NON BLOCK**/} } return false; } /** * {@inheritDoc} */ @Override public void onAsyncStart() { runOnUiThread(new Runnable() { @Override public void run() { SearchActivity.this.toggleResults(false, false); } }); } /** * {@inheritDoc} */ @Override public void onAsyncEnd(boolean cancelled) { this.mSearchListView.post(new Runnable() { @Override public void run() { try { //Dismiss the dialog if (SearchActivity.this.mDialog != null) { SearchActivity.this.mDialog.dismiss(); } // Resolve the symlinks FileHelper.resolveSymlinks( SearchActivity.this, SearchActivity.this.mResultList); // Draw the results drawResults(); } catch (Throwable ex) { Log.e(TAG, "onAsyncEnd method fails", ex); //$NON-NLS-1$ } } }); } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public void onPartialResult(final Object partialResults) { //Saved in the global result list, for save at the end if (partialResults instanceof FileSystemObject) { SearchActivity.this.mResultList.add((FileSystemObject)partialResults); } else { SearchActivity.this.mResultList.addAll((List<FileSystemObject>)partialResults); } //Notify progress this.mSearchListView.post(new Runnable() { @Override public void run() { if (SearchActivity.this.mDialog != null) { int progress = SearchActivity.this.mResultList.size(); setProgressMsg(progress); } } }); } /** * {@inheritDoc} */ @Override public void onAsyncExitCode(int exitCode) {/**NON BLOCK**/} /** * {@inheritDoc} */ @Override public void onException(Exception cause) { //Capture the exception ExceptionUtil.translateException(this, cause); } /** * Method that draw the results in the listview * @hide */ void drawResults() { //Toggle results this.toggleResults(this.mResultList.size() > 0, true); setFoundItems(this.mResultList.size(), this.mSearchDirectory); //Create the task for drawing the data this.mDrawingSearchResultTask = new SearchResultDrawingAsyncTask( this.mSearchListView, this.mSearchWaiting, this.mResultList, this.mQuery); this.mDrawingSearchResultTask.execute(); } /** * Method that creates a {@link SearchInfoParcelable} reference from * the current data. * * @return SearchInfoParcelable The search info reference */ private SearchInfoParcelable createSearchInfo() { SearchInfoParcelable parcel = new SearchInfoParcelable(); parcel.setSearchDirectory(this.mSearchDirectory); parcel.setSearchResultList( ((SearchResultAdapter)this.mSearchListView.getAdapter()).getData()); parcel.setSearchQuery(this.mQuery); return parcel; } /** * Method that set the progress of the search * * @param progress The progress * @hide */ void setProgressMsg(int progress) { String msg = getResources().getQuantityString( R.plurals.search_found_items, progress, Integer.valueOf(progress)); SearchActivity.this.mDialog.setProgress(Html.fromHtml(msg)); } /** * Method that applies the current theme to the activity * @hide */ void applyTheme() { Theme theme = ThemeManager.getCurrentTheme(this); theme.setBaseTheme(this, false); //- ActionBar theme.setTitlebarDrawable(this, getActionBar(), "titlebar_drawable"); //$NON-NLS-1$ View v = getActionBar().getCustomView().findViewById(R.id.customtitle_title); theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$ v = findViewById(R.id.ab_button1); theme.setImageDrawable(this, (ImageView)v, "ic_config_drawable"); //$NON-NLS-1$ // ContentView theme.setBackgroundDrawable( this, getWindow().getDecorView(), "background_drawable"); //$NON-NLS-1$ //- StatusBar v = findViewById(R.id.search_status); theme.setBackgroundDrawable(this, v, "statusbar_drawable"); //$NON-NLS-1$ v = findViewById(R.id.search_status_found_items); theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$ v = findViewById(R.id.search_status_query_terms); theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$ //ListView if (this.mSearchListView.getAdapter() != null) { ((SearchResultAdapter)this.mSearchListView.getAdapter()).notifyDataSetChanged(); } this.mSearchListView.setDivider( theme.getDrawable(this, "horizontal_divider_drawable")); //$NON-NLS-1$ this.mSearchListView.invalidate(); } }