SearchManagerService.java revision d2d6014f715f12f6263f61ba3eeb6f8cba6d0fa6
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17package android.server.search; 18 19import android.app.ActivityManagerNative; 20import android.app.IActivityWatcher; 21import android.app.ISearchManager; 22import android.app.ISearchManagerCallback; 23import android.app.SearchManager; 24import android.content.BroadcastReceiver; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.os.Bundle; 30import android.os.Handler; 31import android.os.RemoteException; 32import android.util.Log; 33 34import java.util.List; 35 36/** 37 * The search manager service handles the search UI, and maintains a registry of searchable 38 * activities. 39 */ 40public class SearchManagerService extends ISearchManager.Stub { 41 42 // general debugging support 43 private static final String TAG = "SearchManagerService"; 44 private static final boolean DBG = false; 45 46 // Context that the service is running in. 47 private final Context mContext; 48 49 // This field is initialized in ensureSearchablesCreated(), and then never modified. 50 // Only accessed by ensureSearchablesCreated() and getSearchables() 51 private Searchables mSearchables; 52 53 // This field is initialized in ensureSearchDialogCreated(), and then never modified. 54 // Only accessed by ensureSearchDialogCreated() and getSearchDialog() 55 private SearchDialogWrapper mSearchDialog; 56 57 /** 58 * Initializes the Search Manager service in the provided system context. 59 * Only one instance of this object should be created! 60 * 61 * @param context to use for accessing DB, window manager, etc. 62 */ 63 public SearchManagerService(Context context) { 64 mContext = context; 65 // call initialize() after all pending actions on the main system thread have finished 66 new Handler().post(new Runnable() { 67 public void run() { 68 initialize(); 69 } 70 }); 71 } 72 73 /** 74 * Initializes the list of searchable activities and the search UI. 75 */ 76 void initialize() { 77 try { 78 ActivityManagerNative.getDefault().registerActivityWatcher( 79 mActivityWatcher); 80 } catch (RemoteException e) { 81 } 82 } 83 84 private synchronized void ensureSearchablesCreated() { 85 if (mSearchables != null) return; // already created 86 87 mSearchables = new Searchables(mContext); 88 mSearchables.buildSearchableList(); 89 90 IntentFilter packageFilter = new IntentFilter(); 91 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 92 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 93 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 94 packageFilter.addDataScheme("package"); 95 mContext.registerReceiver(mPackageChangedReceiver, packageFilter); 96 } 97 98 private synchronized void ensureSearchDialogCreated() { 99 if (mSearchDialog != null) return; 100 101 mSearchDialog = new SearchDialogWrapper(mContext); 102 } 103 104 private synchronized Searchables getSearchables() { 105 ensureSearchablesCreated(); 106 return mSearchables; 107 } 108 109 private synchronized SearchDialogWrapper getSearchDialog() { 110 ensureSearchDialogCreated(); 111 return mSearchDialog; 112 } 113 114 /** 115 * Refreshes the "searchables" list when packages are added/removed. 116 */ 117 private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() { 118 @Override 119 public void onReceive(Context context, Intent intent) { 120 String action = intent.getAction(); 121 122 if (Intent.ACTION_PACKAGE_ADDED.equals(action) || 123 Intent.ACTION_PACKAGE_REMOVED.equals(action) || 124 Intent.ACTION_PACKAGE_CHANGED.equals(action)) { 125 if (DBG) Log.d(TAG, "Got " + action); 126 // Dismiss search dialog, since the search context may no longer be valid 127 getSearchDialog().stopSearch(); 128 // Update list of searchable activities 129 getSearchables().buildSearchableList(); 130 broadcastSearchablesChanged(); 131 } 132 } 133 }; 134 135 private IActivityWatcher.Stub mActivityWatcher = new IActivityWatcher.Stub() { 136 public void activityResuming(int activityId) throws RemoteException { 137 if (DBG) Log.i("foo", "********************** resuming: " + activityId); 138 if (mSearchDialog == null) return; 139 mSearchDialog.activityResuming(activityId); 140 } 141 public void closingSystemDialogs(String reason) { 142 if (DBG) Log.i("foo", "********************** closing dialogs: " + reason); 143 if (mSearchDialog == null) return; 144 mSearchDialog.closingSystemDialogs(reason); 145 } 146 }; 147 148 /** 149 * Informs all listeners that the list of searchables has been updated. 150 */ 151 void broadcastSearchablesChanged() { 152 mContext.sendBroadcast( 153 new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED)); 154 } 155 156 // 157 // Searchable activities API 158 // 159 160 /** 161 * Returns the SearchableInfo for a given activity. 162 * 163 * @param launchActivity The activity from which we're launching this search. 164 * @param globalSearch If false, this will only launch the search that has been specifically 165 * defined by the application (which is usually defined as a local search). If no default 166 * search is defined in the current application or activity, no search will be launched. 167 * If true, this will always launch a platform-global (e.g. web-based) search instead. 168 * @return Returns a SearchableInfo record describing the parameters of the search, 169 * or null if no searchable metadata was available. 170 */ 171 public SearchableInfo getSearchableInfo(final ComponentName launchActivity, 172 final boolean globalSearch) { 173 if (globalSearch) { 174 return getSearchables().getDefaultSearchable(); 175 } else { 176 if (launchActivity == null) { 177 Log.e(TAG, "getSearchableInfo(), activity == null"); 178 return null; 179 } 180 return getSearchables().getSearchableInfo(launchActivity); 181 } 182 } 183 184 /** 185 * Returns a list of the searchable activities that can be included in global search. 186 */ 187 public List<SearchableInfo> getSearchablesInGlobalSearch() { 188 return getSearchables().getSearchablesInGlobalSearchList(); 189 } 190 191 /** 192 * Returns a list of the searchable activities that handle web searches. 193 * Can be called from any thread. 194 */ 195 public List<SearchableInfo> getSearchablesForWebSearch() { 196 return getSearchables().getSearchablesForWebSearchList(); 197 } 198 199 /** 200 * Returns the default searchable activity for web searches. 201 * Can be called from any thread. 202 */ 203 public SearchableInfo getDefaultSearchableForWebSearch() { 204 return getSearchables().getDefaultSearchableForWebSearch(); 205 } 206 207 /** 208 * Sets the default searchable activity for web searches. 209 * Can be called from any thread. 210 */ 211 public void setDefaultWebSearch(final ComponentName component) { 212 getSearchables().setDefaultWebSearch(component); 213 broadcastSearchablesChanged(); 214 } 215 216 // Search UI API 217 218 /** 219 * Launches the search UI. Can be called from any thread. 220 * 221 * @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean) 222 */ 223 public void startSearch(String initialQuery, 224 boolean selectInitialQuery, 225 ComponentName launchActivity, 226 Bundle appSearchData, 227 boolean globalSearch, 228 ISearchManagerCallback searchManagerCallback, 229 int ident) { 230 getSearchDialog().startSearch(initialQuery, 231 selectInitialQuery, 232 launchActivity, 233 appSearchData, 234 globalSearch, 235 searchManagerCallback, 236 ident, 237 false); // don't trigger 238 } 239 240 /** 241 * Launches the search UI and triggers the search, as if the user had clicked on the 242 * search button within the dialog. 243 * 244 * @see SearchManager#triggerSearch(String, android.content.ComponentName, android.os.Bundle) 245 */ 246 public void triggerSearch(String query, 247 ComponentName launchActivity, 248 Bundle appSearchData, 249 ISearchManagerCallback searchManagerCallback, 250 boolean globalSearch, 251 int ident) { 252 getSearchDialog().startSearch( 253 query, 254 false, 255 launchActivity, 256 appSearchData, 257 globalSearch, 258 searchManagerCallback, 259 ident, 260 true); // triger search after launching 261 } 262 263 /** 264 * Cancels the search dialog. Can be called from any thread. 265 */ 266 public void stopSearch() { 267 getSearchDialog().stopSearch(); 268 } 269 270 public boolean isVisible() { 271 return mSearchDialog != null && mSearchDialog.isVisible(); 272 } 273 274} 275