summaryrefslogtreecommitdiffstats
path: root/core/java/android/server/search/SearchManagerService.java
diff options
context:
space:
mode:
authorBjorn Bringert <bringert@android.com>2009-07-06 21:32:50 +0100
committerBjorn Bringert <bringert@android.com>2009-07-08 17:43:49 +0100
commit444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8 (patch)
tree5deb7155bf79ae9001f9d742c905fac2baf7fd9c /core/java/android/server/search/SearchManagerService.java
parente9ac96f76d513d2cee50dfea7d70b14669643ba9 (diff)
downloadframeworks_base-444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8.zip
frameworks_base-444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8.tar.gz
frameworks_base-444c727e0eecf83e9d0b9c4e7af5cbf5fc4135f8.tar.bz2
Run search UI on its own thread.
Details: - Add a new SearchDialogWrapper class that makes sure all access to the SearchDialog is run one a single thread other than the main ServerThread. - Don't save/restore seach dialog state in Activity. This resulted in lots of calls to the SearchManager throughout the life cycle of all activities, for the questionable benefit of restoring the search dialog in a few cases. - Remove search UI state save/restore, and the isVisible() method from SearchManagerService. They are no longer used, and were tricky to implement since they return values from the search UI thread to the service. - Handle configuration changes in searchDialogWrapper instead of calling through from Activity. Fixes http://b/issue?id=1938101 TODO: - Activity.performPause() calls stopSearch(). This call may not happen until the new activity has been started. If the new activity starts a search immediately, this search could be cancelled by the old activity's call top stopSearch().
Diffstat (limited to 'core/java/android/server/search/SearchManagerService.java')
-rw-r--r--core/java/android/server/search/SearchManagerService.java353
1 files changed, 93 insertions, 260 deletions
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 373e61f..87adfb3 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -18,52 +18,38 @@ package android.server.search;
import android.app.ISearchManager;
import android.app.ISearchManagerCallback;
-import android.app.SearchDialog;
import android.app.SearchManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
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.Handler;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.text.TextUtils;
import android.util.Log;
import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
/**
- * This is a simplified version of the Search Manager service. It no longer handles
- * presentation (UI). Its function is to maintain the map & list of "searchable"
- * items, which provides a mapping from individual activities (where a user might have
- * invoked search) to specific searchable activities (where the search will be dispatched).
+ * The search manager service handles the search UI, and maintains a registry of searchable
+ * activities.
*/
-public class SearchManagerService extends ISearchManager.Stub
- implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener
-{
- // general debugging support
+public class SearchManagerService extends ISearchManager.Stub {
+
+ // general debugging support
private static final String TAG = "SearchManagerService";
private static final boolean DBG = false;
- // class maintenance and general shared data
+ // Context that the service is running in.
private final Context mContext;
- private final Handler mHandler;
- private boolean mSearchablesDirty;
- private final Searchables mSearchables;
-
- final SearchDialog mSearchDialog;
- ISearchManagerCallback mCallback = null;
- private final boolean mDisabledOnBoot;
+ // This field is initialized in initialize(), and then never modified.
+ // It is volatile since it can be accessed by multiple threads.
+ private volatile Searchables mSearchables;
- private static final String DISABLE_SEARCH_PROPERTY = "dev.disablesearchdialog";
+ // This field is initialized in initialize(), and then never modified.
+ // It is volatile since it can be accessed by multiple threads.
+ private volatile SearchDialogWrapper mSearchDialog;
/**
* Initializes the Search Manager service in the provided system context.
@@ -73,82 +59,71 @@ public class SearchManagerService extends ISearchManager.Stub
*/
public SearchManagerService(Context context) {
mContext = context;
- mHandler = new Handler();
- mSearchablesDirty = true;
- mSearchables = new Searchables(context);
- mSearchDialog = new SearchDialog(context);
- mSearchDialog.setOnCancelListener(this);
- mSearchDialog.setOnDismissListener(this);
+ // call initialize() after all pending actions on the main system thread have finished
+ new Handler().post(new Runnable() {
+ public void run() {
+ initialize();
+ }
+ });
+ }
- // Setup the infrastructure for updating and maintaining the list
- // of searchable activities.
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addDataScheme("package");
- mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+ /**
+ * Initializes the search UI and the list of searchable activities.
+ */
+ void initialize() {
+ mSearchables = createSearchables();
+ mSearchDialog = new SearchDialogWrapper(mContext);
+ }
+
+ private Searchables createSearchables() {
+ Searchables searchables = new Searchables(mContext);
+ searchables.buildSearchableList();
- // After startup settles down, preload the searchables list,
- // which will reduce the delay when the search UI is invoked.
- mHandler.post(mRunUpdateSearchable);
+ IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addDataScheme("package");
+ mContext.registerReceiver(mPackageChangedReceiver, packageFilter);
- // allows disabling of search dialog for stress testing runs
- mDisabledOnBoot = !TextUtils.isEmpty(SystemProperties.get(DISABLE_SEARCH_PROPERTY));
+ return searchables;
}
/**
- * Listens for intent broadcasts.
- *
- * The primary purpose here is to refresh the "searchables" list
- * if packages are added/removed.
+ * Refreshes the "searchables" list when packages are added/removed.
*/
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- // First, test for intents that matter at any time
- if (action.equals(Intent.ACTION_PACKAGE_ADDED) ||
- action.equals(Intent.ACTION_PACKAGE_REMOVED) ||
- action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
- mSearchablesDirty = true;
- mHandler.post(mRunUpdateSearchable);
- return;
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
+ Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+ Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ if (DBG) Log.d(TAG, "Got " + action);
+ // Dismiss search dialog, since the search context may no longer be valid
+ mSearchDialog.stopSearch();
+ // Update list of searchable activities
+ mSearchables.buildSearchableList();
+ broadcastSearchablesChanged();
}
}
};
/**
- * This runnable (for the main handler / UI thread) will update the searchables list.
- */
- private Runnable mRunUpdateSearchable = new Runnable() {
- public void run() {
- updateSearchablesIfDirty();
- }
- };
-
- /**
- * Updates the list of searchables, either at startup or in response to
- * a package add/remove broadcast message.
+ * Informs all listeners that the list of searchables has been updated.
*/
- private void updateSearchables() {
- if (DBG) debug("updateSearchables()");
- mSearchables.buildSearchableList();
- mSearchablesDirty = false;
+ void broadcastSearchablesChanged() {
+ mContext.sendBroadcast(
+ new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED));
}
- /**
- * Updates the list of searchables if needed.
- */
- private void updateSearchablesIfDirty() {
- if (mSearchablesDirty) {
- updateSearchables();
- }
- }
+ //
+ // Searchable activities API
+ //
/**
- * Returns the SearchableInfo for a given activity
+ * Returns the SearchableInfo for a given activity.
*
* @param launchActivity The activity from which we're launching this search.
* @param globalSearch If false, this will only launch the search that has been specifically
@@ -158,226 +133,84 @@ public class SearchManagerService extends ISearchManager.Stub
* @return Returns a SearchableInfo record describing the parameters of the search,
* or null if no searchable metadata was available.
*/
- public SearchableInfo getSearchableInfo(ComponentName launchActivity, boolean globalSearch) {
- updateSearchablesIfDirty();
- SearchableInfo si = null;
+ public SearchableInfo getSearchableInfo(final ComponentName launchActivity,
+ final boolean globalSearch) {
+ if (mSearchables == null) return null;
if (globalSearch) {
- si = mSearchables.getDefaultSearchable();
+ return mSearchables.getDefaultSearchable();
} else {
if (launchActivity == null) {
Log.e(TAG, "getSearchableInfo(), activity == null");
return null;
}
- si = mSearchables.getSearchableInfo(launchActivity);
+ return mSearchables.getSearchableInfo(launchActivity);
}
-
- return si;
}
/**
* Returns a list of the searchable activities that can be included in global search.
*/
public List<SearchableInfo> getSearchablesInGlobalSearch() {
- updateSearchablesIfDirty();
+ if (mSearchables == null) return null;
return mSearchables.getSearchablesInGlobalSearchList();
}
- /**
- * Launches the search UI on the main thread of the service.
- *
- * @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean)
- */
- public void startSearch(final String initialQuery,
- final boolean selectInitialQuery,
- final ComponentName launchActivity,
- final Bundle appSearchData,
- final boolean globalSearch,
- final ISearchManagerCallback searchManagerCallback) {
- if (DBG) debug("startSearch()");
- Runnable task = new Runnable() {
- public void run() {
- performStartSearch(initialQuery,
- selectInitialQuery,
- launchActivity,
- appSearchData,
- globalSearch,
- searchManagerCallback);
- }
- };
- mHandler.post(task);
- }
-
- /**
- * Actually launches the search. This must be called on the service UI thread.
- */
- /*package*/ void performStartSearch(String initialQuery,
- boolean selectInitialQuery,
- ComponentName launchActivity,
- Bundle appSearchData,
- boolean globalSearch,
- ISearchManagerCallback searchManagerCallback) {
- if (DBG) debug("performStartSearch()");
-
- if (mDisabledOnBoot) {
- Log.d(TAG, "ignoring start search request because " + DISABLE_SEARCH_PROPERTY
- + " system property is set.");
- return;
- }
-
- mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
- globalSearch);
- if (searchManagerCallback != null) {
- mCallback = searchManagerCallback;
- }
- }
-
- /**
- * Cancels the search dialog. Can be called from any thread.
- */
- public void stopSearch() {
- if (DBG) debug("stopSearch()");
- mHandler.post(new Runnable() {
- public void run() {
- performStopSearch();
- }
- });
- }
-
- /**
- * Cancels the search dialog. Must be called from the service UI thread.
- */
- /*package*/ void performStopSearch() {
- if (DBG) debug("performStopSearch()");
- mSearchDialog.cancel();
- }
-
- /**
- * Determines if the Search UI is currently displayed.
- *
- * @see SearchManager#isVisible()
- */
- public boolean isVisible() {
- return postAndWait(mIsShowing, false, "isShowing()");
- }
-
- private final Callable<Boolean> mIsShowing = new Callable<Boolean>() {
- public Boolean call() {
- return mSearchDialog.isShowing();
- }
- };
-
- public Bundle onSaveInstanceState() {
- return postAndWait(mOnSaveInstanceState, null, "onSaveInstanceState()");
- }
-
- private final Callable<Bundle> mOnSaveInstanceState = new Callable<Bundle>() {
- public Bundle call() {
- if (mSearchDialog.isShowing()) {
- return mSearchDialog.onSaveInstanceState();
- } else {
- return null;
- }
- }
- };
-
- public void onRestoreInstanceState(final Bundle searchDialogState) {
- if (searchDialogState != null) {
- mHandler.post(new Runnable() {
- public void run() {
- mSearchDialog.onRestoreInstanceState(searchDialogState);
- }
- });
- }
- }
-
- public void onConfigurationChanged(final Configuration newConfig) {
- mHandler.post(new Runnable() {
- public void run() {
- if (mSearchDialog.isShowing()) {
- mSearchDialog.onConfigurationChanged(newConfig);
- }
- }
- });
- }
-
- /**
- * Called by {@link SearchDialog} when it goes away.
- */
- public void onDismiss(DialogInterface dialog) {
- if (DBG) debug("onDismiss()");
- if (mCallback != null) {
- try {
- mCallback.onDismiss();
- } catch (RemoteException ex) {
- Log.e(TAG, "onDismiss() failed: " + ex);
- }
- }
- }
-
- /**
- * Called by {@link SearchDialog} when the user or activity cancels search.
- * When this is called, {@link #onDismiss} is called too.
- */
- public void onCancel(DialogInterface dialog) {
- if (DBG) debug("onCancel()");
- if (mCallback != null) {
- try {
- mCallback.onCancel();
- } catch (RemoteException ex) {
- Log.e(TAG, "onCancel() failed: " + ex);
- }
- }
- }
/**
* Returns a list of the searchable activities that handle web searches.
+ * Can be called from any thread.
*/
public List<SearchableInfo> getSearchablesForWebSearch() {
- updateSearchablesIfDirty();
+ if (mSearchables == null) return null;
return mSearchables.getSearchablesForWebSearchList();
}
/**
* Returns the default searchable activity for web searches.
+ * Can be called from any thread.
*/
public SearchableInfo getDefaultSearchableForWebSearch() {
- updateSearchablesIfDirty();
+ if (mSearchables == null) return null;
return mSearchables.getDefaultSearchableForWebSearch();
}
/**
* Sets the default searchable activity for web searches.
+ * Can be called from any thread.
*/
- public void setDefaultWebSearch(ComponentName component) {
+ public void setDefaultWebSearch(final ComponentName component) {
+ if (mSearchables == null) return;
mSearchables.setDefaultWebSearch(component);
+ broadcastSearchablesChanged();
}
+ // Search UI API
+
/**
- * Runs an operation on the handler for the service, blocks until it returns,
- * and returns the value returned by the operation.
+ * Launches the search UI. Can be called from any thread.
*
- * @param <V> Return value type.
- * @param callable Operation to run.
- * @param errorResult Value to return if the operations throws an exception.
- * @param name Operation name to include in error log messages.
- * @return The value returned by the operation.
+ * @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean)
*/
- private <V> V postAndWait(Callable<V> callable, V errorResult, String name) {
- FutureTask<V> task = new FutureTask<V>(callable);
- mHandler.post(task);
- try {
- return task.get();
- } catch (InterruptedException ex) {
- Log.e(TAG, "Error calling " + name + ": " + ex);
- return errorResult;
- } catch (ExecutionException ex) {
- Log.e(TAG, "Error calling " + name + ": " + ex);
- return errorResult;
- }
+ public void startSearch(String initialQuery,
+ boolean selectInitialQuery,
+ ComponentName launchActivity,
+ Bundle appSearchData,
+ boolean globalSearch,
+ ISearchManagerCallback searchManagerCallback) {
+ if (mSearchDialog == null) return;
+ mSearchDialog.startSearch(initialQuery,
+ selectInitialQuery,
+ launchActivity,
+ appSearchData,
+ globalSearch,
+ searchManagerCallback);
}
- private static void debug(String msg) {
- Thread thread = Thread.currentThread();
- Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")");
+ /**
+ * Cancels the search dialog. Can be called from any thread.
+ */
+ public void stopSearch() {
+ if (mSearchDialog == null) return;
+ mSearchDialog.stopSearch();
}
}