blob: f9a0b93c97ba21a4f4f4babee2c2ff63aaa33459 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source 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 android.server.search;
import android.app.ActivityManagerNative;
import android.app.IActivityWatcher;
import android.app.ISearchManager;
import android.app.ISearchManagerCallback;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import java.util.List;
/**
* The search manager service handles the search UI, and maintains a registry of searchable
* activities.
*/
public class SearchManagerService extends ISearchManager.Stub {
// general debugging support
private static final String TAG = "SearchManagerService";
private static final boolean DBG = false;
// Context that the service is running in.
private final Context mContext;
// This field is initialized in ensureSearchablesCreated(), and then never modified.
// Only accessed by ensureSearchablesCreated() and getSearchables()
private Searchables mSearchables;
// This field is initialized in ensureSearchDialogCreated(), and then never modified.
// Only accessed by ensureSearchDialogCreated() and getSearchDialog()
private SearchDialogWrapper mSearchDialog;
/**
* Initializes the Search Manager service in the provided system context.
* Only one instance of this object should be created!
*
* @param context to use for accessing DB, window manager, etc.
*/
public SearchManagerService(Context context) {
mContext = context;
// call initialize() after all pending actions on the main system thread have finished
new Handler().post(new Runnable() {
public void run() {
initialize();
}
});
}
/**
* Initializes the list of searchable activities and the search UI.
*/
void initialize() {
try {
ActivityManagerNative.getDefault().registerActivityWatcher(
mActivityWatcher);
} catch (RemoteException e) {
}
}
private synchronized void ensureSearchablesCreated() {
if (mSearchables != null) return; // already created
mSearchables = new Searchables(mContext);
mSearchables.buildSearchableList();
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);
}
private synchronized void ensureSearchDialogCreated() {
if (mSearchDialog != null) return;
mSearchDialog = new SearchDialogWrapper(mContext);
}
private synchronized Searchables getSearchables() {
ensureSearchablesCreated();
return mSearchables;
}
private synchronized SearchDialogWrapper getSearchDialog() {
ensureSearchDialogCreated();
return mSearchDialog;
}
/**
* Refreshes the "searchables" list when packages are added/removed.
*/
private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
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
getSearchDialog().stopSearch();
// Update list of searchable activities
getSearchables().buildSearchableList();
broadcastSearchablesChanged();
}
}
};
private IActivityWatcher.Stub mActivityWatcher = new IActivityWatcher.Stub() {
public void activityResuming(int activityId) throws RemoteException {
if (DBG) Log.i("foo", "********************** resuming: " + activityId);
if (mSearchDialog == null) return;
mSearchDialog.activityResuming(activityId);
}
public void closingSystemDialogs(String reason) {
if (DBG) Log.i("foo", "********************** closing dialogs: " + reason);
if (mSearchDialog == null) return;
mSearchDialog.closingSystemDialogs(reason);
}
};
/**
* Informs all listeners that the list of searchables has been updated.
*/
void broadcastSearchablesChanged() {
Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
mContext.sendBroadcast(intent);
}
//
// Searchable activities API
//
/**
* 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
* defined by the application (which is usually defined as a local search). If no default
* search is defined in the current application or activity, no search will be launched.
* If true, this will always launch a platform-global (e.g. web-based) search instead.
* @return Returns a SearchableInfo record describing the parameters of the search,
* or null if no searchable metadata was available.
*/
public SearchableInfo getSearchableInfo(final ComponentName launchActivity,
final boolean globalSearch) {
if (globalSearch) {
return getSearchables().getDefaultSearchable();
} else {
if (launchActivity == null) {
Log.e(TAG, "getSearchableInfo(), activity == null");
return null;
}
return getSearchables().getSearchableInfo(launchActivity);
}
}
/**
* Returns a list of the searchable activities that can be included in global search.
*/
public List<SearchableInfo> getSearchablesInGlobalSearch() {
return getSearchables().getSearchablesInGlobalSearchList();
}
/**
* Returns a list of the searchable activities that handle web searches.
* Can be called from any thread.
*/
public List<SearchableInfo> getSearchablesForWebSearch() {
return getSearchables().getSearchablesForWebSearchList();
}
/**
* Returns the default searchable activity for web searches.
* Can be called from any thread.
*/
public SearchableInfo getDefaultSearchableForWebSearch() {
return getSearchables().getDefaultSearchableForWebSearch();
}
/**
* Sets the default searchable activity for web searches.
* Can be called from any thread.
*/
public void setDefaultWebSearch(final ComponentName component) {
getSearchables().setDefaultWebSearch(component);
broadcastSearchablesChanged();
}
// Search UI API
/**
* Launches the search UI. Can be called from any thread.
*
* @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean)
*/
public void startSearch(String initialQuery,
boolean selectInitialQuery,
ComponentName launchActivity,
Bundle appSearchData,
boolean globalSearch,
ISearchManagerCallback searchManagerCallback,
int ident) {
getSearchDialog().startSearch(initialQuery,
selectInitialQuery,
launchActivity,
appSearchData,
globalSearch,
searchManagerCallback,
ident,
false); // don't trigger
}
/**
* Launches the search UI and triggers the search, as if the user had clicked on the
* search button within the dialog.
*
* @see SearchManager#triggerSearch(String, android.content.ComponentName, android.os.Bundle)
*/
public void triggerSearch(String query,
ComponentName launchActivity,
Bundle appSearchData,
ISearchManagerCallback searchManagerCallback,
int ident) {
getSearchDialog().startSearch(
query,
false,
launchActivity,
appSearchData,
false,
searchManagerCallback,
ident,
true); // triger search after launching
}
/**
* Cancels the search dialog. Can be called from any thread.
*/
public void stopSearch() {
getSearchDialog().stopSearch();
}
public boolean isVisible() {
return mSearchDialog != null && mSearchDialog.isVisible();
}
}