2 * Copyright (C) 2012 The CyanogenMod Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.cyanogenmod.filemanager.activities;
19 import android.app.ActionBar;
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.app.SearchManager;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.res.Configuration;
29 import android.os.Bundle;
30 import android.os.Parcelable;
31 import android.preference.PreferenceActivity;
32 import android.provider.SearchRecentSuggestions;
33 import android.text.Html;
34 import android.text.TextUtils;
35 import android.util.Log;
36 import android.view.KeyEvent;
37 import android.view.MenuItem;
38 import android.view.View;
39 import android.widget.AdapterView;
40 import android.widget.AdapterView.OnItemClickListener;
41 import android.widget.AdapterView.OnItemLongClickListener;
42 import android.widget.ImageView;
43 import android.widget.ListAdapter;
44 import android.widget.ListView;
45 import android.widget.ProgressBar;
46 import android.widget.TextView;
47 import android.widget.Toast;
49 import com.cyanogenmod.filemanager.FileManagerApplication;
50 import com.cyanogenmod.filemanager.R;
51 import com.cyanogenmod.filemanager.activities.preferences.SearchPreferenceFragment;
52 import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences;
53 import com.cyanogenmod.filemanager.adapters.SearchResultAdapter;
54 import com.cyanogenmod.filemanager.commands.AsyncResultExecutable;
55 import com.cyanogenmod.filemanager.commands.AsyncResultListener;
56 import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
57 import com.cyanogenmod.filemanager.console.RelaunchableException;
58 import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
59 import com.cyanogenmod.filemanager.model.Directory;
60 import com.cyanogenmod.filemanager.model.FileSystemObject;
61 import com.cyanogenmod.filemanager.model.ParentDirectory;
62 import com.cyanogenmod.filemanager.model.Query;
63 import com.cyanogenmod.filemanager.model.SearchResult;
64 import com.cyanogenmod.filemanager.model.Symlink;
65 import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
66 import com.cyanogenmod.filemanager.preferences.AccessMode;
67 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
68 import com.cyanogenmod.filemanager.preferences.Preferences;
69 import com.cyanogenmod.filemanager.providers.RecentSearchesContentProvider;
70 import com.cyanogenmod.filemanager.tasks.SearchResultDrawingAsyncTask;
71 import com.cyanogenmod.filemanager.ui.ThemeManager;
72 import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
73 import com.cyanogenmod.filemanager.ui.dialogs.ActionsDialog;
74 import com.cyanogenmod.filemanager.ui.dialogs.MessageProgressDialog;
75 import com.cyanogenmod.filemanager.ui.policy.DeleteActionPolicy;
76 import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
77 import com.cyanogenmod.filemanager.ui.widgets.ButtonItem;
78 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView;
79 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerListener;
80 import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
81 import com.cyanogenmod.filemanager.util.CommandHelper;
82 import com.cyanogenmod.filemanager.util.DialogHelper;
83 import com.cyanogenmod.filemanager.util.ExceptionUtil;
84 import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
85 import com.cyanogenmod.filemanager.util.FileHelper;
86 import com.cyanogenmod.filemanager.util.StorageHelper;
88 import java.io.FileNotFoundException;
89 import java.util.ArrayList;
90 import java.util.List;
93 * An activity for search files and folders.
95 public class SearchActivity extends Activity
96 implements AsyncResultListener, OnItemClickListener,
97 OnItemLongClickListener, OnRequestRefreshListener {
99 private static final String TAG = "SearchActivity"; //$NON-NLS-1$
101 private static boolean DEBUG = false;
104 * An {@link Intent} action for restore view information.
106 public static final String ACTION_RESTORE =
107 "com.cyanogenmod.filemanager.activities.SearchActivity#Restore"; //$NON-NLS-1$
110 * Intent extra parameter for search in the selected directory on enter.
112 public static final String EXTRA_SEARCH_DIRECTORY = "extra_search_directory"; //$NON-NLS-1$
115 * Intent extra parameter for pass the restore information.
117 public static final String EXTRA_SEARCH_RESTORE = "extra_search_restore"; //$NON-NLS-1$
120 //Minimum characters to allow query
121 private static final int MIN_CHARS_SEARCH = 3;
123 private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
125 public void onReceive(Context context, Intent intent) {
126 if (intent != null) {
127 if (intent.getAction().compareTo(
128 FileManagerSettings.INTENT_SETTING_CHANGED) == 0) {
130 // The settings has changed
131 String key = intent.getStringExtra(
132 FileManagerSettings.EXTRA_SETTING_CHANGED_KEY);
134 if (SearchActivity.this.mSearchListView.getAdapter() != null &&
137 SETTINGS_HIGHLIGHT_TERMS.getId()) == 0 ||
140 SETTINGS_SHOW_RELEVANCE_WIDGET.getId()) == 0 ||
143 SETTINGS_SORT_SEARCH_RESULTS_MODE.getId()) == 0)) {
145 // Recreate the adapter
146 int pos = SearchActivity.
147 this.mSearchListView.getFirstVisiblePosition();
149 SearchActivity.this.mSearchListView.setSelection(pos);
153 } else if (intent.getAction().compareTo(
154 FileManagerSettings.INTENT_THEME_CHANGED) == 0) {
162 * A listener for flinging events from {@link FlingerListView}
164 private final OnItemFlingerListener mOnItemFlingerListener = new OnItemFlingerListener() {
167 public boolean onItemFlingerStart(
168 AdapterView<?> parent, View view, int position, long id) {
170 // Response if the item can be removed
171 SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter();
172 SearchResult result = adapter.getItem(position);
173 if (result != null && result.getFso() != null) {
174 if (result.getFso() instanceof ParentDirectory) {
175 // This is not possible ...
180 } catch (Exception e) {
181 ExceptionUtil.translateException(SearchActivity.this, e, true, false);
187 public void onItemFlingerEnd(OnItemFlingerResponder responder,
188 AdapterView<?> parent, View view, int position, long id) {
191 // Response if the item can be removed
192 SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter();
193 SearchResult result = adapter.getItem(position);
194 if (result != null && result.getFso() != null) {
195 DeleteActionPolicy.removeFileSystemObject(
204 // Cancels the flinger operation
207 } catch (Exception e) {
208 ExceptionUtil.translateException(SearchActivity.this, e, true, false);
217 MessageProgressDialog mDialog = null;
221 AsyncResultExecutable mExecutable = null;
226 ListView mSearchListView;
230 ProgressBar mSearchWaiting;
234 TextView mSearchFoundItems;
238 TextView mSearchTerms;
239 private View mEmptyListMsg;
241 private String mSearchDirectory;
245 List<FileSystemObject> mResultList;
254 SearchInfoParcelable mRestoreState;
256 private SearchResultDrawingAsyncTask mDrawingSearchResultTask;
268 protected void onCreate(Bundle state) {
270 Log.d(TAG, "SearchActivity.onCreate"); //$NON-NLS-1$
273 // Check if app is running in chrooted mode
274 this.mChRooted = FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
276 // Register the broadcast receiver
277 IntentFilter filter = new IntentFilter();
278 filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED);
279 filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED);
280 registerReceiver(this.mNotificationReceiver, filter);
282 // Set the theme before setContentView
283 Theme theme = ThemeManager.getCurrentTheme(this);
284 theme.setBaseTheme(this, false);
287 overridePendingTransition(R.anim.translate_to_right_in, R.anim.hold_out);
289 //Set the main layout of the activity
290 setContentView(R.layout.search);
297 //Initialize action bars and search
298 initTitleActionBar();
301 // Apply current theme
304 if (this.mRestoreState != null) {
305 //Restore activity from cached data
309 if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) {
311 } else if (ACTION_RESTORE.equals(getIntent().getAction())) {
312 restoreState(getIntent().getExtras());
318 super.onCreate(state);
325 protected void onDestroy() {
327 Log.d(TAG, "SearchActivity.onDestroy"); //$NON-NLS-1$
330 // Unregister the receiver
332 unregisterReceiver(this.mNotificationReceiver);
333 } catch (Throwable ex) {
337 //All destroy. Continue
345 public void onConfigurationChanged(Configuration newConfig) {
346 super.onConfigurationChanged(newConfig);
353 protected void onNewIntent(Intent intent) {
355 if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) {
364 protected void onPause() {
366 overridePendingTransition(R.anim.hold_in, R.anim.translate_to_left_out);
374 protected void onSaveInstanceState(Bundle outState) {
376 Log.d(TAG, "SearchActivity.onSaveInstanceState"); //$NON-NLS-1$
379 super.onSaveInstanceState(outState);
383 * Method that save the instance of the activity.
385 * @param state The current state of the activity
387 private void saveState(Bundle state) {
389 if (this.mSearchListView.getAdapter() != null) {
390 state.putParcelable(EXTRA_SEARCH_RESTORE, createSearchInfo());
392 } catch (Throwable ex) {
393 Log.w(TAG, "The state can't be saved", ex); //$NON-NLS-1$
398 * Method that restore the instance of the activity.
400 * @param state The previous state of the activity
402 private void restoreState(Bundle state) {
404 if (state.containsKey(EXTRA_SEARCH_RESTORE)) {
405 this.mRestoreState = state.getParcelable(EXTRA_SEARCH_RESTORE);
407 } catch (Throwable ex) {
408 Log.w(TAG, "The state can't be restored", ex); //$NON-NLS-1$
413 * Method that initializes the titlebar of the activity.
415 private void initTitleActionBar() {
416 //Configure the action bar options
417 getActionBar().setBackgroundDrawable(
418 getResources().getDrawable(R.drawable.bg_holo_titlebar));
419 getActionBar().setDisplayOptions(
420 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
421 getActionBar().setDisplayHomeAsUpEnabled(true);
422 View customTitle = getLayoutInflater().inflate(R.layout.simple_customtitle, null, false);
424 TextView title = (TextView)customTitle.findViewById(R.id.customtitle_title);
425 title.setText(R.string.search);
426 title.setContentDescription(getString(R.string.search));
427 ButtonItem configuration = (ButtonItem)customTitle.findViewById(R.id.ab_button1);
428 configuration.setImageResource(R.drawable.ic_holo_light_config);
429 configuration.setVisibility(View.VISIBLE);
431 getActionBar().setCustomView(customTitle);
435 * Method that initializes the component of the activity.
437 private void initComponents() {
439 this.mEmptyListMsg = findViewById(R.id.search_empty_msg);
441 this.mSearchListView = (ListView)findViewById(R.id.search_listview);
442 this.mSearchListView.setOnItemClickListener(this);
443 this.mSearchListView.setOnItemLongClickListener(this);
445 // If we should set the listview to response to flinger gesture detection
447 Preferences.getSharedPreferences().getBoolean(
448 FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
449 ((Boolean)FileManagerSettings.
450 SETTINGS_USE_FLINGER.
451 getDefaultValue()).booleanValue());
453 ((FlingerListView)this.mSearchListView).
454 setOnItemFlingerListener(this.mOnItemFlingerListener);
458 this.mSearchWaiting = (ProgressBar)findViewById(R.id.search_waiting);
459 this.mSearchFoundItems = (TextView)findViewById(R.id.search_status_found_items);
460 setFoundItems(0, ""); //$NON-NLS-1$
461 this.mSearchTerms = (TextView)findViewById(R.id.search_status_query_terms);
462 this.mSearchTerms.setText(
463 Html.fromHtml(getString(R.string.search_terms, ""))); //$NON-NLS-1$
467 * Method invoked when an action item is clicked.
469 * @param view The button pushed
471 public void onActionBarItemClick(View view) {
472 switch (view.getId()) {
473 case R.id.ab_button1:
475 Intent settings = new Intent(this, SettingsPreferences.class);
477 PreferenceActivity.EXTRA_SHOW_FRAGMENT,
478 SearchPreferenceFragment.class.getName());
479 startActivity(settings);
488 * Method that initializes the titlebar of the activity.
490 private void initSearch() {
491 //Stop any pending action
493 if (SearchActivity.this.mDrawingSearchResultTask != null
494 && SearchActivity.this.mDrawingSearchResultTask.isRunning()) {
495 SearchActivity.this.mDrawingSearchResultTask.cancel(true);
497 } catch (Throwable ex2) {
501 if (SearchActivity.this.mDialog != null) {
502 SearchActivity.this.mDialog.dismiss();
504 } catch (Throwable ex2) {
508 //Recovery the search directory
509 Bundle bundle = getIntent().getBundleExtra(SearchManager.APP_DATA);
510 //If data is not present, use root directory to do the search
511 this.mSearchDirectory = FileHelper.ROOT_DIRECTORY;
512 if (bundle != null) {
513 this.mSearchDirectory =
514 bundle.getString(EXTRA_SEARCH_DIRECTORY, FileHelper.ROOT_DIRECTORY);
517 //Retrieve the query ¿from voice recognizer?
518 boolean voiceQuery = true;
519 List<String> userQueries =
520 getIntent().getStringArrayListExtra(android.speech.RecognizerIntent.EXTRA_RESULTS);
521 if (userQueries == null || userQueries.size() == 0) {
523 userQueries = new ArrayList<String>();
524 //Recovers and save the last term search in the memory
525 Preferences.setLastSearch(getIntent().getStringExtra(SearchManager.QUERY));
526 userQueries.add(Preferences.getLastSearch());
530 //Filter the queries? Needed if queries come from voice recognition
531 final List<String> filteredUserQueries =
532 (voiceQuery) ? filterQuery(userQueries) : userQueries;
535 this.mQuery = new Query().fillSlots(filteredUserQueries);
536 List<String> queries = this.mQuery.getQueries();
538 //Check if some queries has lower than allowed, in this case
539 //request the user for stop the search
541 int cc = queries.size();
542 for (int i = 0; i < cc; i++) {
543 if (queries.get(i).trim().length() < MIN_CHARS_SEARCH) {
549 askUserBeforeSearch(voiceQuery, this.mQuery, this.mSearchDirectory);
551 doSearch(voiceQuery, this.mQuery, this.mSearchDirectory);
557 * Method that ask the user before do the search.
559 * @param voiceQuery Indicates if the query is from voice recognition
560 * @param query The terms of the search
561 * @param searchDirectory The directory of the search
563 private void askUserBeforeSearch(
564 final boolean voiceQuery, final Query query, final String searchDirectory) {
565 //Show a dialog asking the user
567 DialogHelper.createYesNoDialog(
569 R.string.search_few_characters_title,
570 R.string.search_few_characters_msg,
571 new DialogInterface.OnClickListener() {
573 public void onClick(DialogInterface alertDialog, int which) {
574 if (which == DialogInterface.BUTTON_POSITIVE) {
575 doSearch(voiceQuery, query, searchDirectory);
579 //Close search activity
580 back(true, null, false);
583 DialogHelper.delegateDialogShow(this, dialog);
587 * Method that do the search.
589 * @param voiceQuery Indicates if the query is from voice recognition
590 * @param query The terms of the search
591 * @param searchDirectory The directory of the search
595 final boolean voiceQuery, final Query query, final String searchDirectory) {
597 // Recovers the user preferences about save suggestions
598 boolean saveSuggestions = Preferences.getSharedPreferences().getBoolean(
599 FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS.getId(),
600 ((Boolean)FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS.
601 getDefaultValue()).booleanValue());
602 if (saveSuggestions) {
603 //Save every query for use as recent suggestions
604 SearchRecentSuggestions suggestions =
605 new SearchRecentSuggestions(this,
606 RecentSearchesContentProvider.AUTHORITY,
607 RecentSearchesContentProvider.MODE);
609 List<String> queries = query.getQueries();
610 int cc = queries.size();
611 for (int i = 0; i < cc; i++) {
612 suggestions.saveRecentQuery(queries.get(i), null);
618 if (this.mSearchListView.getAdapter() != null) {
619 ((SearchResultAdapter)this.mSearchListView.getAdapter()).dispose();
621 this.mResultList = new ArrayList<FileSystemObject>();
622 SearchResultAdapter adapter =
623 new SearchResultAdapter(this,
624 new ArrayList<SearchResult>(), R.layout.search_item, this.mQuery);
625 this.mSearchListView.setAdapter(adapter);
628 this.mSearchTerms.setText(
629 Html.fromHtml(getString(R.string.search_terms, query.getTerms())));
631 //Now, do the search in background
632 this.mSearchListView.post(new Runnable() {
636 //Retrieve the terms of the search
637 String label = getString(R.string.searching_action_label);
639 //Show a dialog for the progress
640 SearchActivity.this.mDialog =
641 new MessageProgressDialog(
644 R.string.searching, label, true);
648 // Set the cancel listener
649 SearchActivity.this.mDialog.setOnCancelListener(
650 new MessageProgressDialog.OnCancelListener() {
652 public boolean onCancel() {
653 //User has requested the cancellation of the search
654 //Broadcast the cancellation
655 if (!SearchActivity.this.mExecutable.isCancelled()) {
656 if (SearchActivity.this.mExecutable.cancel()) {
657 ListAdapter listAdapter =
659 this.mSearchListView.getAdapter();
660 if (listAdapter != null) {
661 SearchActivity.this.toggleResults(
662 listAdapter.getCount() > 0, true);
671 SearchActivity.this.mDialog.show();
673 //Execute the query (search are process in background)
674 SearchActivity.this.mExecutable =
675 CommandHelper.findFiles(
678 SearchActivity.this.mQuery,
682 } catch (Throwable ex) {
683 //Remove all elements
685 SearchActivity.this.removeAll();
686 } catch (Throwable ex2) {
690 if (SearchActivity.this.mDialog != null) {
691 SearchActivity.this.mDialog.dismiss();
693 } catch (Throwable ex2) {
697 //Capture the exception
698 Log.e(TAG, "Search failed", ex); //$NON-NLS-1$
699 DialogHelper.showToast(
701 R.string.search_error_msg, Toast.LENGTH_SHORT);
702 SearchActivity.this.mSearchListView.setVisibility(View.GONE);
709 * Method that restore the activity from the cached data.
711 private void loadFromCacheData() {
712 this.mSearchListView.post(new Runnable() {
716 List<SearchResult> list = SearchActivity.this.mRestoreState.getSearchResultList();
717 String directory = SearchActivity.this.mRestoreState.getSearchDirectory();
718 SearchActivity.this.toggleResults(list.size() > 0, true);
719 setFoundItems(list.size(), directory);
722 Query query = SearchActivity.this.mRestoreState.getSearchQuery();
724 TextUtils.join(" | ", //$NON-NLS-1$;
725 query.getQueries().toArray(new String[]{}));
726 if (terms.endsWith(" | ")) { //$NON-NLS-1$;
727 terms = ""; //$NON-NLS-1$;
729 SearchActivity.this.mSearchTerms.setText(
730 Html.fromHtml(getString(R.string.search_terms, terms)));
733 if (SearchActivity.this.mSearchWaiting != null) {
734 SearchActivity.this.mSearchWaiting.setVisibility(View.VISIBLE);
737 //Add list to the listview
738 if (SearchActivity.this.mSearchListView.getAdapter() != null) {
739 ((SearchResultAdapter)SearchActivity.this.
740 mSearchListView.getAdapter()).clear();
742 SearchResultAdapter adapter =
743 new SearchResultAdapter(
744 SearchActivity.this.mSearchListView.getContext(),
746 R.layout.search_item,
748 SearchActivity.this.mSearchListView.setAdapter(adapter);
749 SearchActivity.this.mSearchListView.setSelection(0);
751 SearchActivity.this.mQuery = query;
752 SearchActivity.this.mSearchDirectory = mRestoreState.getSearchDirectory();
754 } catch (Throwable ex) {
755 //Capture the exception
756 ExceptionUtil.translateException(SearchActivity.this, ex);
760 if (SearchActivity.this.mSearchWaiting != null) {
761 SearchActivity.this.mSearchWaiting.setVisibility(View.GONE);
769 * Method that filter the user queries for valid queries only.<br/>
771 * Only allow query strings with more that 3 characters
773 * @param original The original user queries
774 * @return List<String> The list of queries filtered
776 @SuppressWarnings("static-method")
777 private List<String> filterQuery(List<String> original) {
778 List<String> dst = new ArrayList<String>(original);
780 for (int i = cc - 1; i >= 0; i--) {
781 String query = dst.get(i);
782 if (query == null || query.trim().length() < MIN_CHARS_SEARCH) {
790 * Method that removes all items and display a message.
794 SearchResultAdapter adapter = (SearchResultAdapter)this.mSearchListView.getAdapter();
796 this.mSearchListView.setSelection(0);
797 toggleResults(false, true);
801 * Method that toggle the views when there are results.
803 * @param hasResults Indicates if there are results
804 * @param showEmpty Show the empty list message
807 void toggleResults(boolean hasResults, boolean showEmpty) {
808 this.mSearchListView.setVisibility(hasResults ? View.VISIBLE : View.INVISIBLE);
809 this.mEmptyListMsg.setVisibility(!hasResults && showEmpty ? View.VISIBLE : View.INVISIBLE);
813 * Method that display the number of found items.
815 * @param items The number of items
816 * @param searchDirectory The search directory path
819 void setFoundItems(final int items, final String searchDirectory) {
820 if (this.mSearchFoundItems != null) {
821 this.mSearchFoundItems.post(new Runnable() {
824 String directory = searchDirectory;
825 if (SearchActivity.this.mChRooted &&
826 directory != null && directory.length() > 0) {
827 directory = StorageHelper.getChrootedPath(directory);
833 R.plurals.search_found_items, items, Integer.valueOf(items));
834 SearchActivity.this.mSearchFoundItems.setText(
836 R.string.search_found_items_in_directory,
848 public boolean onKeyUp(int keyCode, KeyEvent event) {
850 case KeyEvent.KEYCODE_BACK:
851 back(true, null, false);
854 return super.onKeyUp(keyCode, event);
862 public boolean onOptionsItemSelected(MenuItem item) {
863 switch (item.getItemId()) {
864 case android.R.id.home:
865 back(true, null, false);
868 return super.onOptionsItemSelected(item);
876 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
878 SearchResult result = ((SearchResultAdapter)parent.getAdapter()).getItem(position);
879 FileSystemObject fso = result.getFso();
880 if (fso instanceof Directory) {
881 back(false, fso, false);
883 } else if (fso instanceof Symlink) {
884 Symlink symlink = (Symlink)fso;
885 if (symlink.getLinkRef() != null && symlink.getLinkRef() instanceof Directory) {
886 back(false, symlink.getLinkRef(), false);
889 fso = symlink.getLinkRef();
892 // Open the file with the preferred registered app
893 back(false, fso, false);
895 } catch (Throwable ex) {
896 ExceptionUtil.translateException(this.mSearchListView.getContext(), ex);
904 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
905 // Different actions depending on user preference
907 // Get the adapter, the search result and the fso
908 SearchResultAdapter adapter = ((SearchResultAdapter)parent.getAdapter());
909 SearchResult searchResult = adapter.getItem(position);
910 FileSystemObject fso = searchResult.getFso();
912 // Open the actions menu
914 return true; //Always consume the event
918 * Method invoked when a request to show the menu associated
919 * with an item is started.
921 * @param item The item for which the request was started
923 public void onRequestMenu(FileSystemObject item) {
924 // Prior to show the dialog, refresh the item reference
925 FileSystemObject fso = null;
927 fso = CommandHelper.getFileInfo(this, item.getFullPath(), false, null);
929 throw new NoSuchFileOrDirectory(item.getFullPath());
932 } catch (Exception e) {
934 ExceptionUtil.translateException(this, e);
937 if (e instanceof FileNotFoundException || e instanceof NoSuchFileOrDirectory) {
943 ActionsDialog dialog = new ActionsDialog(this, null, fso, false, true);
944 dialog.setOnRequestRefreshListener(this);
949 * Method that removes the {@link FileSystemObject} reference
951 * @param fso The file system object
953 private void removeItem(FileSystemObject fso) {
954 SearchResultAdapter adapter =
955 (SearchResultAdapter)this.mSearchListView.getAdapter();
956 if (adapter != null) {
957 int pos = adapter.getPosition(fso);
959 SearchResult sr = adapter.getItem(pos);
964 toggleResults(adapter.getCount() > 0, true);
965 setFoundItems(adapter.getCount(), this.mSearchDirectory);
973 public void onRequestRefresh(Object o, boolean clearSelection) {
974 // Refresh only the item
975 SearchResultAdapter adapter =
976 (SearchResultAdapter)this.mSearchListView.getAdapter();
977 if (adapter != null) {
978 if (o instanceof FileSystemObject) {
980 FileSystemObject fso = (FileSystemObject)o;
981 int pos = adapter.getPosition(fso);
983 SearchResult sr = adapter.getItem(pos);
986 } else if (o == null) {
988 List<SearchResult> results = adapter.getData();
989 this.mResultList = new ArrayList<FileSystemObject>(results.size());
990 int cc = results.size();
991 for (int i = 0; i < cc; i++) {
992 this.mResultList.add(results.get(i).getFso());
1003 public void onRequestRemove(Object o, boolean clearSelection) {
1004 if (o instanceof FileSystemObject) {
1005 removeItem((FileSystemObject)o);
1013 public void onNavigateTo(Object o) {
1014 if (o instanceof FileSystemObject) {
1015 back(false, (FileSystemObject)o, true);
1020 * Method that returns to previous activity.
1022 * @param cancelled Indicates if the activity was cancelled
1023 * @param item The fso
1024 * @param isChecked If the fso was fully retrieve previously to this call. Otherwise, a
1025 * getFileInfo call is done to complete the fso information
1028 void back(final boolean cancelled, FileSystemObject item, boolean isChecked) {
1029 final Context ctx = SearchActivity.this;
1030 final Intent intent = new Intent();
1031 boolean finish = true;
1033 if (SearchActivity.this.mDrawingSearchResultTask != null
1034 && SearchActivity.this.mDrawingSearchResultTask.isRunning()) {
1035 SearchActivity.this.mDrawingSearchResultTask.cancel(true);
1037 if (this.mRestoreState != null) {
1039 NavigationActivity.EXTRA_SEARCH_LAST_SEARCH_DATA,
1040 (Parcelable)this.mRestoreState);
1042 setResult(RESULT_CANCELED, intent);
1044 // Check that the bookmark exists
1045 FileSystemObject fso = item;
1048 fso = CommandHelper.getFileInfo(ctx, item.getFullPath(), null);
1050 finish = navigateTo(fso, intent);
1052 } catch (Exception e) {
1053 // Capture the exception
1054 final FileSystemObject fFso = fso;
1055 final OnRelaunchCommandResult relaunchListener = new OnRelaunchCommandResult() {
1057 public void onSuccess() {
1058 if (navigateTo(fFso, intent)) {
1063 public void onFailed(Throwable cause) {
1064 ExceptionUtil.translateException(ctx, cause, false, false);
1067 public void onCancelled() { /** NON BLOCK**/}
1069 ExceptionUtil.translateException(ctx, e, false, true, relaunchListener);
1070 if (!(e instanceof RelaunchableException)) {
1071 if (e instanceof NoSuchFileOrDirectory || e instanceof FileNotFoundException) {
1072 // The fso not exists, delete the fso from the search
1075 } catch (Exception ex) {/**NON BLOCK**/}
1082 // End this activity
1089 * Method invoked when the activity needs to exit
1091 private void exit() {
1092 if (this.mSearchListView.getAdapter() != null) {
1093 ((SearchResultAdapter)this.mSearchListView.getAdapter()).dispose();
1099 * Method that navigate to the file system used the intent (NavigationActivity)
1101 * @param fso The file system object to navigate to
1102 * @param intent The intent used to navigate to
1103 * @return boolean If the action implies finish this activity
1105 boolean navigateTo(FileSystemObject fso, Intent intent) {
1107 if (FileHelper.isDirectory(fso)) {
1108 intent.putExtra(NavigationActivity.EXTRA_SEARCH_ENTRY_SELECTION, fso);
1110 NavigationActivity.EXTRA_SEARCH_LAST_SEARCH_DATA,
1111 (Parcelable)createSearchInfo());
1112 setResult(RESULT_OK, intent);
1116 // Open the file here, so when focus back to the app, the search activity
1117 // its in top of the stack
1118 IntentsActionPolicy.openFileSystemObject(this, fso, false, null, null);
1120 // The fso not exists, delete the fso from the search
1123 } catch (Exception ex) {/**NON BLOCK**/}
1132 public void onAsyncStart() {
1133 runOnUiThread(new Runnable() {
1136 SearchActivity.this.toggleResults(false, false);
1145 public void onAsyncEnd(boolean cancelled) {
1146 this.mSearchListView.post(new Runnable() {
1150 //Dismiss the dialog
1151 if (SearchActivity.this.mDialog != null) {
1152 SearchActivity.this.mDialog.dismiss();
1155 // Resolve the symlinks
1156 FileHelper.resolveSymlinks(
1157 SearchActivity.this, SearchActivity.this.mResultList);
1162 } catch (Throwable ex) {
1163 Log.e(TAG, "onAsyncEnd method fails", ex); //$NON-NLS-1$
1173 @SuppressWarnings("unchecked")
1174 public void onPartialResult(final Object partialResults) {
1175 //Saved in the global result list, for save at the end
1176 if (partialResults instanceof FileSystemObject) {
1177 SearchActivity.this.mResultList.add((FileSystemObject)partialResults);
1179 SearchActivity.this.mResultList.addAll((List<FileSystemObject>)partialResults);
1183 this.mSearchListView.post(new Runnable() {
1186 if (SearchActivity.this.mDialog != null) {
1187 int progress = SearchActivity.this.mResultList.size();
1188 setProgressMsg(progress);
1198 public void onAsyncExitCode(int exitCode) {/**NON BLOCK**/}
1204 public void onException(Exception cause) {
1205 //Capture the exception
1206 ExceptionUtil.translateException(this, cause);
1210 * Method that draw the results in the listview
1213 void drawResults() {
1215 this.toggleResults(this.mResultList.size() > 0, true);
1216 setFoundItems(this.mResultList.size(), this.mSearchDirectory);
1218 //Create the task for drawing the data
1219 this.mDrawingSearchResultTask =
1220 new SearchResultDrawingAsyncTask(
1221 this.mSearchListView,
1222 this.mSearchWaiting,
1225 this.mDrawingSearchResultTask.execute();
1229 * Method that creates a {@link SearchInfoParcelable} reference from
1232 * @return SearchInfoParcelable The search info reference
1234 private SearchInfoParcelable createSearchInfo() {
1235 SearchInfoParcelable parcel = new SearchInfoParcelable();
1236 parcel.setSearchDirectory(this.mSearchDirectory);
1237 parcel.setSearchResultList(
1238 ((SearchResultAdapter)this.mSearchListView.getAdapter()).getData());
1239 parcel.setSearchQuery(this.mQuery);
1244 * Method that set the progress of the search
1246 * @param progress The progress
1249 void setProgressMsg(int progress) {
1251 getResources().getQuantityString(
1252 R.plurals.search_found_items,
1254 Integer.valueOf(progress));
1255 SearchActivity.this.mDialog.setProgress(Html.fromHtml(msg));
1259 * Method that applies the current theme to the activity
1263 Theme theme = ThemeManager.getCurrentTheme(this);
1264 theme.setBaseTheme(this, false);
1267 theme.setTitlebarDrawable(this, getActionBar(), "titlebar_drawable"); //$NON-NLS-1$
1268 View v = getActionBar().getCustomView().findViewById(R.id.customtitle_title);
1269 theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
1270 v = findViewById(R.id.ab_button1);
1271 theme.setImageDrawable(this, (ImageView)v, "ic_config_drawable"); //$NON-NLS-1$
1273 theme.setBackgroundDrawable(
1274 this, getWindow().getDecorView(), "background_drawable"); //$NON-NLS-1$
1276 v = findViewById(R.id.search_status);
1277 theme.setBackgroundDrawable(this, v, "statusbar_drawable"); //$NON-NLS-1$
1278 v = findViewById(R.id.search_status_found_items);
1279 theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
1280 v = findViewById(R.id.search_status_query_terms);
1281 theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
1283 if (this.mSearchListView.getAdapter() != null) {
1284 ((SearchResultAdapter)this.mSearchListView.getAdapter()).notifyDataSetChanged();
1286 this.mSearchListView.setDivider(
1287 theme.getDrawable(this, "horizontal_divider_drawable")); //$NON-NLS-1$
1288 this.mSearchListView.invalidate();