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